Convert SQL statement to NHibernate Query - c#

I tried to ask this question yesterday, but I don't think my wording was very clear.
Therefore, I've tried to reword the question below.
We have an existing system which we are rewriting in MVC4, and we are using NHibernate 3.2 in our new system.
In the old system, we build up a SQL statement similar to this:
SELECT myField as series,
pstatus,
Year(acc_date) AS year,
Month(acc_date) AS month,
COUNT(CAST(reportable AS INT)) AS totalreportable,
SUM(CAST(reportable AS INT)) AS riddorreportable,
SUM(CAST(lt_acc AS INT)) AS losttime,
SUM(CAST(acc_losttime AS INT)) AS totaldayslost,
SUM(CAST(nearmiss AS INT)) AS nearmiss
FROM incident
WHERE 1=1
In the above, we will be replacing "WHERE 1=1" with nHibernate criteria (which we already have in the system to filter data).
myField in the above SQL statement is a field which is selectable by the user e.g. he could choose Department, City etc.
What I need, therefore, is information on how to use nHibernate to achieve the same result as the above SQL statement.

Since you are already using the ICriteria API to construct the WHERE clause, you'll need to use the ICriteria's SetProjection() method in order to build up your dynamic SELECT clause.
This will allow you to use your user's selected field in a dynamic way by using strings.
I was going to write a code example but I felt that someone had already done a very good job of it on this other StackOverflow question: https://stackoverflow.com/a/696020/670028
Official NHibernate documentation on ICriteria Projections: http://nhibernate.info/doc/nh/en/index.html#querycriteria-projection

Chapter 14, 15, 16, and 17 in the documentation in about the different query APIs:
http://nhibernate.info/doc/nh/en/index.html
In addition to those there is also support for LINQ, which nowadays should probably be your preferred query API unless you hit its limitations. If you are building a new system, you should start with NH 3.3.2 to have access to the latest LINQ improvements.
All the query APIs can be made to select (project) different properties based on some user input. I think you may need to be more specific with what you have tried or what problem you experience for a more detailed response.

Related

Distinguish EF query and LINQ query

I am new to Entity Framework. I am bit confused with the difference in EF query and LINQ query. I have two tables and related queries listed below. Can you please tell me whether all these queries are LINQ or any of them are in EF? I need to write EF queries for selecting entire row, few columns and also joins. Your help or any relevant links would be highly appreciated.
Product_Details table
Product_ID, Product_Name, Price, Item_Desc, Stock_Avaialble, Created_Date
Sales_Details Table
Sales_ID, Product_ID, Qunatity, Total_Amont
var result = context.ProductDetails
where ProductID == 10
select new {ProductID, ProductName, Price}
var result = from prod in context.ProductDetails
where ProductID == 10
select new {ProductID, ProductName, Price}
var result = context.ProductDetails
.Where(p=>p.ProductID == 10)
.Select(p=> new Prod(p.ProductID, p.ProductName, p.Price))
var result1 = from prod in context.ProductDetails
join sales in context.SalesDetails on prod.ProductID == sales.ProductID
select new {prod.ProductID, prod.ProductName, sales.Qunatity, sales.TotalAmount}
Thanks
Peter
There is no such thing as an E.F. query. The queries you use when working with E.F. are LINQ. LINQ however does a whole lot more.
From E.F. documentation:
Entity Framework is an object-relational mapper (O/RM) that enables .NET developers to work with a database using .NET objects. It eliminates the need for most of the data-access code that developers usually need to write.
Entity Framework is a library used in C#.
From this article about LINQ on MSDN:
We use the term language-integrated query (LINQ) to indicate that query is an integrated feature of the developer's primary programming languages.
LINQ is part of C#.
General-purpose query facilities added to the .NET Framework apply to all sources of information, not just relational or XML data. This facility is called .NET Language-Integrated Query (LINQ).
The key here is the phrase "apply to all sources of information". All E.F. does is abstract away databases, and allow you to work with them as if they were normal enumerables in your program.
The standard query operators allow queries to be applied to any IEnumerable-based information source.
You use LINQ queries and extension methods for enumerables that LINQ provides to work with the "enumerables" (DB abstractions) that E.F. provides you with.
LINQ is a way of querying within you language of choice (VB, C#, .NET). It isn't directly related to EF. EF is something that maps to your database and you use LINQ as a way to query the database. It's just syntax that you use paired with EF to get data. You can also use LINQ on things such as collections.
As far as I understand your question, all your code returns queries associated with linq. They define operations to be done in the database but have not taken the trip to the db yet because they don't define execute commands(find, singleordefault, first, tolist etc). Linq is used for building and executing queries, like an addition to the EF language that also can do that but has limited usage. For databases linq builds queries and access the db via EF(or another ORM).
Some syntax like AsNoTracking(),Include(), ThenInclude() etc are EF syntax exclusively (meaning you have to refrence that library). Using linq syntax means you have to refrence linq(although most templates include it by default).

Expression Tree for .NET DateTime <> Oracle Timestamp like search

I need to know whether I can get a clue or not.
I am trying to build an Expression Tree that is evaluated via Entity Framework 6 (LINQ to Entities) via ODP.NET managed driver (current Version) to the appropriate Oracle statement in order to perform filtering. This works perfectly for string <> nvarchar2 datatypes. The output for a Contains search is something like:
Select *
[...]
FROM "MYSCHEMA"."MYTABLE" "Extent1"
WHERE ("Extent1"."ANVARCHAR2COLUM" LIKE '%givenStrValueToFind%')
Now I want to get the same result for a DateTime <> Timestamp(6) since this line of sql statement is valid for an oracle query and returns all Dates which contains '08':
select * from "MYSCHEMA"."MYTABLE" where ATIMESTAMP6COLUMN like '%08%';
Since I am new to Expression Trees, I first need to know (after googling alot and tried this an that), whether this is possible before I dig deeper into. And if so, how could this be accomplished best? Since there is no Contains Method defined for DateTime and DateTime? Maybe providing Extension methods? And I dont want to execude queries directly against database.
Any hints would be nice to 'unhook' the given database datatype perhaps...
Thx.
For EF 6 you can use :
ctx.MYTABLE.Where(x=>x.ATIMESTAMP6COLUMN.ToString().Contains("08")).ToList();
which translates to
SELECT
[Extent1].[some property name] AS [some property name],
[Extent1].[ATIMESTAMP6COLUMN] AS [ATIMESTAMP6COLUMN],
.
.
.
FROM [dbo].[MYTABLE] AS [Extent1]
WHERE CAST( [Extent1].[ATIMESTAMP6COLUMN] AS nvarchar(max)) LIKE N'%08%'
As far as what I know all versions of EF does not support string functions but EF 6.x supports this kind of functions(The EF that I tested with is EF 6.1.3, the test is done with sql server localdb as DBMS).
I don't know if I understood. Anyway, you don't need to build an expression tree to avoid DB load in memory. LINQ build an expression tree from an expression, EF translates the expression tree to a SQL statement (using the EF Provider) then the query is run on the DB.
In your case the LINQ Query should be something like this
myContext.MYTABLE.Where(e => e.ATIMESTAMP6COLUMN.Contains("08").ToList();
EDIT
Conversions:
You can use .ToString(). It should be a canonical function (function available on every EF Provider).
Other DateTime related functions:
You could have a look to other canonical functions (there are some about dates that allow to retrieve date parts) and also to non canonical functions (functions implemented only in EF Provider for oracle).
References:
Canonical functions https://msdn.microsoft.com/en-us/library/vstudio/bb738626(v=vs.100).aspx
Actually I can't find non canonical functions for Oracle.
If you need it the best thing is that you ask another question.

How to query many-to-many relationship with 'AND' condition using LINQ and Entity Framework

My current database solution includes three tables called Establishment, Feature, and a linking many-to-many table called EstablishmentFeature (since an establishment can have many features, and a feature can exists across multiple establishments).
I need to generate a query that returns establishments that meet only certain criteria, namely, which establishments have X features based on a collection of featureId's being passed in. The establishment must have ALL features that are being search, i.e.. AND not OR condition.
I got the SQL to achieve the desired result, but I am pulling my hair out trying to work out the LINQ (lambra prefereably) equivalent. The T-SQL is:
SELECT e.[EstablishmentId], e.[Name], e.[Description]
FROM Establishment e
INNER JOIN EstablishmentFeature ef
ON e.[EstablishmentId] = ef.[EstablishmentId]
WHERE ef.[FeatureId] in ('20', '15', '72')
GROUP BY e.[EstablishmentId], e.[Name], e.[Description]
HAVING COUNT(*) = 3
I tried to use Linqer to convert the SQL but Linqer crashes when it attempts the conversion. I tried reinstalling Linqer, but it crashes without fail when trying to compile the LINQ syntax. (Simpler conversions work though). Also tried to work out the LINQ equivalent using LinqPad, but I just ended up chasing my tail...
Is this something I will have to use PredicateBuilder for? Somewhat exhausted, I don't want to go through the PredicateBuilder learning curve if there is a simple solution that is escaping me.
I'd try this (For all given ids there is any (= at least one) feature that has this given id):
var establishments = context.Establishments
.Where(e => featureIds.All(fid => e.Features.Any(f => f.FeatureId == fid)))
.ToList();
(featureIds is an IEnumerable<int> with the Ids being searched for.)

What is the difference between NHibernate Query<> vs QueryOver<>?

I just started with NHibernate (using SQLite) in my current project and I mostly used Query<>, because I was familiar writing db queries in Linq.
When I was confronted with some more complex queries, I did some research on QueryOver<> and figured that it should be favored over Query<> because "QueryOver syntax is NH specific". Also, there seems to be nothing that Query<> can do that QueryOver<> can't accomplish.
So I began replacing all usages of Query<> accordingly. It wasn't long before I had the first "issue" where using Query<> seemed just more convenient.
Example (select highest value from column CustomNumber in table BillingDataEntity):
int result = Session.Query<BillingDataEntity>().Select(x => x.CustomNumber).OrderByDescending(a => a).FirstOrDefault();
int result = Session.QueryOver<BillingDataEntity>().Select(x => x.CustomNumber).OrderBy(a => a.CustomNumber).Desc.Take(1).SingleOrDefault<int>();
What I dislike is the need to explicitly cast the result to int and that the the Query<> version is just easier to read. Am i getting the query totally wrong, or in other words: Is there a better way to do it?
I took a look at the generated SQL output:
NHibernate: select billingdat0_.CustomNumber as col_0_0_ from "BillingDataEntity" billingdat0_ order by billingdat0_.CustomNumber desc limit 1
NHibernate: SELECT this_.CustomNumber as y0_ FROM "BillingDataEntity" this_ ORDER BY this_.CustomNumber desc limit #p0;#p0 = 1 [Type: Int32 (0)]
What exactly am i looking at? Is this the "internal" (method dependent) query that NHibernate further translates into the actual database query?
There are plenty of answers regarding QueryOver versus Query here on Stackoverflow but in a nutshell:-
QueryOver is a strongly-typed version of Criteria, and is more
NHibernate specific. Pretty much anything you can do in ICriteria can
be done with QueryOver. In the golden days of ICriteria NH2 you always had
to cast, hence this is why now you need to cast at the end of the
chain back to an int.
LINQ (Query) is a standard query method that works on IQueryable that
doesn't need explicit references to NHibernate and can be considered
more ORM agnostic and therefore follows the linq standard. As you
rightly pointed out you do not need to cast to an int as you are
selecting into the result the customNumber.
I would be very surprised for your simple example if the generated SQL was very different.
I was a big fan of QueryOver but as the Linq provider is getting more mature then 95% of my queries I use Query but for some Nhibernate specific stuff I resort back down to QueryOver. Either way I recommend using a profiling tool to see what you can live with.
Refs: Tradeoffs or versus and versus
About your QueryOver version, I would have written :
int result = Session.QueryOver<BillingDataEntity>()
.Select(Projections.Max<BillingDataEntity>(x => x.CustomNumber))
.SingleOrDefault<int>();
It seems quite readable, and the resulting SQL would be something like :
SELECT max(this_.CustomNumber) as y0_ FROM "BillingDataEntity" this_
Hope this will help

Linq query nhibernate; not supported exception

I'm fairly new to nHibernate having come from an EF background and I'm struggling with the following query :
_patientSearchResultModel = (from patient in _patientRepository.Query(patientSearch.BuildPatientSpecification())
join admission in _admissionRepository.Query(patientSearch.BuildAdmissionSpecification())
on patient.Id equals admission.Patient.Id
orderby admission.AdmissionDate
select new PatientSearchResultModel(patient.Id,
admission.Id,
false,
_phaseTypeMapper.GetPhaseTypeModel(admission.PhaseType),
patient.Last, patient.First,
admission.InPatientLocation,
admission.AdmissionDate,
admission.DischargeDate,
admission.RRI,
null,
admission.CompletionStatus,
admission.FollowupStatus)).ToList();
The intent of this query is to allow users to filter the two queries on parameters built up using the two Build???Specification functions and return the resultset. There could be many admission records and I would only like one PatientSearchResultModel per patient object, with the admission object being the newest one by Admission Date.
These objects are coming from nHibernate and it keeps return a Not Supported exception. There is also an association between Patient and Admissions thus : Patient.Admissions but i couldn't figure out how to then add the query filters return from the function Build???Specifications.
I'd be really grateful if someone could point me in the right direction; am I up against the Linq provider implementation here in nHibernate and need to move to Criteria or is it my Linq query ?
If anyone has any links or suggestions for good books or other learning materials in this area that would also be really helpful too.
I see several potential problems:
If you're using NHibernate 2.x + Linq2NHibernate explicit joins like that are not supported; in other versions they're just considered a smell.
I dont think NHibernate supports calling parameterized constructors in select clauses
I'm very sure NHibernate does not support calling instance methods in the select lambda
I'd suggest using the lambda syntax and SelectMany to alleviate potential join issues. Points #2 & #3 can be solved by projecting into an anonymous type, calling AsEnumerable then projecting into your model type.
Overall I'd suggest restructuring your code like:
var patientSpec = patientSearch.BuildPatientSpecification();
var admissionSpec = patientSearch.BuildAdmissionSpecification();
_patientSearchResultModel = _patientRepository.Where(patientSpec)
.SelectMany(p=>p.Admissions).Where(admissionSpec)
.Select(a=> new {
PatientId = a.Patient.Id,
AdminssionId = a.Id,
a.PhaseType,
a.Patient.Last,
a.Patient.First,
a.InPatientLocation,
a.AdmissionDate,
a.DischargeDate,
a.RRI,
a.CompletionStatus,
a.FollowupStatus
}).AsEnumerable()
.Select(x=> new PatientSearchResultModel(x.PatientId, x.AdmissionId ...))
.ToList();
Divide your query into parts and check which part runs and which doesn't.
My take on this is that select new ... is not supported in Linq to nHibernate.
I would recomend using something else, because it is simply too imature and feature-less to use seriously.
As with most popular LINQ-to-Database query providers, NHibernate will try to translate the whole query into a SQL statement to run against the database. This requires that all elements of your query are possible to express in the SQL flavour you're using.
In your query, the select new statement cannot be expressed in SQL, because you're making a call to the constructor of your PatientSearchResultModel class and are making a call to a GetPhaseTypeModel method.
You should restructure your query to express what you want to execute on the SQL database, then call AsEnumerable() to force the remainder of the query to be evaluated in-memory. After that call, you can call the constructor of your class and any .NET methods, and they will be executed as native code.
This query is too complex to describe it using Linq. It would give wrong result finally (if Patient has more than one admission records, result would have duplicate entries).
I see two steps for solution:
1) At development stage, use in-memory query. So, take Patients using ToList() first (query db at this moment). Some predicates (Patient filter like MRN, First, Last) could be used at this stage.
And then do search in-memory. Not performance, but working solution. Mark it for refactor to optimize later.
2) Finally, use NHibernate IQuery (ISQLQuery) and build sql query manually to make sure it would work as expected and work fast enough on SQL Server side. This is just read-only query and do not require Nhibernate query engine (Linq to Nhibernate) at all.

Categories

Resources