Automatic join with NHibernate / Many-to-one / HQL - c#

I'm working on a .NET C# project on which I had to use NHibernate Mapping Attributes to map my objects to my tables in my database.
Now let's explain what my problem is.
I have two mapped classes, for example ClassA and ClassB. In my database, table A contains a foreign key referencing the primary key of table B. Hence, I have added to ClassA an instance of ClassB mapped in many-to-one:
private ClassB b;
[ManyToOne(0, Name = "B", Column = "ID_TABLE_B_TABLE_A", Class = "ClassB", Update = false, Insert = false)]
public virtual ClassB B
{
get { return b; }
set { b= value; }
}
Now, I want to check the value of a field of ClassB when I'm accessing ClassA. I write the query in HQL:
Session.CreateQuery("select a.Id from ClassA a where a.ClassB.Name = 'xxx' ");
Here's the generated SQL:
select tablea0_.ID_TABLE_A as col_0_0_
from TABLE_A tablea0_, TABLE_B tableb1_
where tablea0_.ID_TABLE_B_TABLE_A = tableb1_.ID_TABLE_B
and tableb1_.NAME_TABLE_B='xxx'
I thought this kind of HQL query was supposed to generated a join statement rather than a where statement, as I have defined a many-to-one association between the two classes. Something like this would've been better:
select tablea0_.ID_TABLE_A as col_0_0_
from TABLE_A tablea0_
left join TABLE_B tableb1_ on tableb1_.ID_TABLE_B = tablea0_.ID_TABLE_B_TABLE_A
where tableb1_.NAME_TABLE_B='xxx'
In my opinion, join looks cleaner to where. I would like to know if there is a way to set up the behaviour of NHibernate accordingly, without specifying the join statement explicitly in the HQL query.
Any help would be appreciated !

The join works like this:
Session.CreateQuery("select a.Id from ClassA a join a.ClassB b where b.Name = 'xxx' ");
Session.CreateQuery("select a.Id from ClassA a left join a.ClassB b where b.Name = 'xxx' ");
Session.CreateQuery("select a.Id from ClassA a left outer join a.ClassB b where b.Name = 'xxx' ");
Of course, it's now for you to figure out which join works best for you. :)
More information # http://docs.jboss.org/hibernate/stable/core/reference/en/html/queryhql.html#queryhql-joins
On a side note, if you want to be one of the cool kids, you could always use Linq to NHibernate:
var result = Session.Linq<ClassA>().Where(a => a.B.Name == 'xxx').ToList();
Would generate an inner join query.

It's been a while since I worked with NHibernate, but I don't think you can force it to do this.
You can try specifying "fetch=join" in the many-to-one mapping, but if memory serves, this only changes the strategy NHibernate uses when fetching related entities in general, not how it translates a custom HQL query to SQL.
But why even worry about it?
In 99% of the cases I actually looked at the generated SQL, it was because incorrect SQL was generated (usually because I made a mistake), or to investigate performance issues.

The generated SQL depends on the datababase dialect. Oracle did not support the ANSI join syntax until 9i so it's possible that the Oracle dialect in NHibernate still uses the old syntax for inner joins. What SQL is produced by a left join? If it's tablea0_.ID_TABLE_B_TABLE_A += tableb1_.ID_TABLE_B then it's using the old syntax for those as well.

Related

OleDB JOIN Syntax Not Correct

I recently asked a question on StackOverflow (MySQL Returns All Rows When field = 0) regarding a query statement not working in MySQL. I now have a very similar problem, this time using OleDB where I am trying to use a join to include fields that have 0 as an entry, but not select every field in the table as a result.
The new look MySQL query posted in the above question as the accepted answer works without a hitch. However the OleDB counterpart I have written to do almost the same does not. It's a bit messy as the tables are not named very well (I didn't create this database) and I'm getting a simple syntax error;
myQuery.CommandText = "SELECT s.scm_num, s.scm_name, c.cr3_text, q.qsp_value, s.scm_bprefix, s.scm_nxbnum FROM qspreset q INNER JOIN sdccomp s LEFT OUTER JOIN cntref3 c ON s.scm_cntref3=c.cr3_id AND q.qsp_relat=s.scm_invtype AND q.qsp_what=13";
I'm querying another table here as well as the two involved in the LEFT OUTER JOIN and I believe that is where I am making the mistake.
Join conditions need to be with the join
myQuery.CommandText =
"SELECT s.scm_num, s.scm_name, c.cr3_text, q.qsp_value, s.scm_bprefix, s.scm_nxbnum
FROM qspreset q
INNER JOIN sdccomp s
on q.qsp_relat = s.scm_invtype AND q.qsp_what = 13
LEFT OUTER JOIN cntref3 c
ON s.scm_cntref3 = c.cr3_id";
q.qsp_what = 13 can be moved to a where
I happen to like this style
In the case of MSSQL T-SQL and some queries with a lot of joins I have gotten more efficient query plan by moving a where condition up into a join. The filter happened early rather that last.
If you don't believe you can put a hard value in a join see SQLfiddle

LINQ Logical join VS inner join

I want to know which one is better for performance:
//Logical
var query = from i in db.Item
from c in db.Category
where i.FK_IdCategory == c.IdCategory
Select new{i.name, c.name};
or
//Join
var query2 = from i in db.Item
join c in db.Category
on c.ID equals i.FK_IdCategory
Select new{i.name, c.name};
Performance of the two queries really depends on which LINQ provider and which RDBMS you're using. Assuming SQL Server, the first would generate the following query:
select i.name, c.name
from Item i, Category c
where i.FK_idCategory = c.IdCategory
Whereas the second would generate:
select i.name, c.name
from Item i
inner join Category c
on i.FK_idCategory = c.IdCategory
Which operate exactly the same in SQL Server as is explained in: Explicit vs implicit SQL joins
This depends on the ORM you're using and how intelligent it is at optimizing your queries for your backend.
Entity Framework can generate some pretty awful SQL if you don't do your linq perfectly, so I'd assume query2 is better.
The only way for you to know for sure would be to inspect the SQL being generated by the two queries.
Eyeballing it, it looks like query1 would result in both tables being pulled in their entirety and then being filtered against each other in your application, while query2 will for sure generate an INNER JOIN in the query, which will let SQL Server do what it does best - set logic.
Is that FK_IdCategory field a member of an actual foreign key index on that table? If not, make it so (and include the name column as an included column in the index) and your query will be very highly performant.
With linq2Sql or EntityFramework, you would probably do something like this:
var query = from i in db.Item
select new {i.name, i.Category.Name}
This will generate a proper SQL inner join.
I do assume that there is a foreign key relation between Item and Category defined.

Lazy loading, eager loading or explicit loading in case of multiple joins?

I am a beginner to entity framework. I have a requirement to fetch many records (sometimes it goes even to millions) from 8 different tables. I am presently joining this tables in linq and converting them to list. (So i am doing explicit loading here) I know whether eager loading will be more appropriate to use in this case for better performance. Any help will be appreciated. Here is my query:
List<FarmerDetailsReport> fdr =
(from fp in mstfp join pd in personalDetails on fp.personDetails.Id equals pd.Id
join ic in identityCertificate on fp.identityCertificate.Id equals ic.Id
join pid in pacsInsuranceData on fp.pacsInsuranceData.Id equals pid.Id into temp
from pid in temp.DefaultIfEmpty()
join bd in bankDetails on fp.bankDetails.Id equals bd.Id
join cd in contactDetails on fp.contactDetails.Id equals cd.Id
join id in incomeDetails on fp.incomeDetails.Id equals id.Id into tmp
from id in tmp.DefaultIfEmpty()
join ua in user
Attributes on fp.UserId equals ua.EmailID
where ((ua.CompanyName == companyName ) && (cd.District == model.DistrictForProfileMIS ) && (cd.Block == model.BlockForProfileMIS) && (bd.bankName == model.BankForProfileMIS ) && Status == "Active")
select new FarmerDetailsReport { .......... }).ToList();
It seems to me that a better way than to join lots of tables in that way in entity framework is to create a view in the database (an indexed view) and query that instead. I'm not a database expert, but it should improve performance in the database as well as the code...
And of course you can change the view if the database changes without having to change your code.
If performance is critical, I would not use a Linq query. The reason for this, is that you do not know what kind of query EF will create. Yes, EF is quite good in creating efficient queries, but if you have a specific case where performance is critical, doing it yourself (if you are an experienced SQL developer) is still the best option.
So I would create an SP in the database.
Update
If you want to execute the SP and returns the result in a POCO objects, one good options is to use Dapper. SP's can be executed very simple:
using Dapper;
using (var connection = MyConnectionFactory.GetConnection()) {
connection.Open();
var data = cnn.Query<MyPocoObject>(
"spMySp",
new { MyParameter = 1 },
commandType: CommandType.StoredProcedure
);
return data;
}
If you are using Entity Framework off course you want to use it's possibilities and not go back to Stored Procedures. Use Include Statements (Eager Loading) or Explizit Loading (.Load()) to get your goal. Thats what EF is made for.

OrderBy a Many To Many relationship with Entity Sql

I'm trying to better utilize the resources of the Entity Sql in the following scenario: I have a table Book which has a Many-To-Many relationship with the Author table. Each book may have from 0 to N authors. I would like to sort the books by the first author name, ie the first record found in this relationship (or null when no authors are linked to a book).
With T-SQL it can be done without difficulty:
SELECT
b.*
FROM
Book AS b
JOIN BookAuthor AS ba ON b.BookId = ba.BookId
JOIN Author AS a ON ba.AuthorId = a.AuthorId
ORDER BY
a.AuthorName;
But I cannot think of how to adapt my code bellow to achieve it. Indeed I don't know how to write something equivalent directly with Entity Sql too.
Entities e = new Entities();
var books = e.Books;
var query = books.Include("Authors");
if (sorting == null)
query = query.OrderBy("it.Title asc");
else
query = query.OrderBy("it.Authors.Name asc"); // This isn't it.
return query.Skip(paging.Skip).Take(paging.Take).ToList();
Could someone explain me how to modify my code to generate the Entity Sql for the desired result? Or even explain me how to write by hand a query using CreateQuery<Book>() to achieve it?
EDIT
Just to elucidate, I'll be working with a very large collection of books (around 100k). Sorting them in memory would be very impactful on the performance. I wish the answers would focus on how to generate the desired ordering using Entity Sql, so the orderby will happens on the database.
The OrderBy method expects you to give it a lambda expression (well, actually a Func delegate, but most people would use lambdas to make them) that can be run to select the field to sort by. Also, OrderBy always orders ascending; if you want descending order there is an OrderByDescending method.
var query = books
.Include("Authors")
.OrderBy(book => book.Authors.Any()
? book.Authors.FirstOrDefault().Name
: string.Empty);
This is basically telling the OrderBy method: "for each book in the sequence, if there are any authors, select the first one's name as my sort key; otherwise, select the empty string. Then return me the books sorted by the sort key."
You could put anything in place of the string.Empty, including for example book.Title or any other property of the book to use in place of the last name for sorting.
EDIT from comments:
As long as the sorting behavior you ask for isn't too complex, the Entity Framework's query provider can usually figure out how to turn it into SQL. It will try really, really hard to do that, and if it can't you'll get a query error. The only time the sorting would be done in client-side objects is if you forced the query to run (e.g. .AsEnumerable()) before the OrderBy was called.
In this case, the EF outputs a select statement that includes the following calculated field:
CASE WHEN ( EXISTS (SELECT
1 AS [C1]
FROM [dbo].[BookAuthor] AS [Extent4]
WHERE [Extent1].[Id] = [Extent4].[Books_Id]
)) THEN [Limit1].[Name] ELSE #p__linq__0 END AS [C1],
Then orders by that.
#p__linq__0 is a parameter, passed in as string.Empty, so you can see it converted the lambda expression into SQL pretty directly. Extent and Limit are just aliases used in the generated SQL for the joined tables etc. Extent1 is [Books] and Limit1 is:
SELECT TOP (1) -- Field list goes here.
FROM [dbo].[BookAuthor] AS [Extent2]
INNER JOIN [dbo].[Authors] AS [Extent3] ON [Extent3].[Id] = [Extent2].[Authors_Id]
WHERE [Extent1].[Id] = [Extent2].[Books_Id]
If you don't care where the sorting is happening (i.e. SQL vs In Code), you can retrieve your result set, and sort it using your own sorting code after the query results have been returned. In my experience, getting specialized sorting like this to work with Entity Framework can be very difficult, frustrating and time consuming.

Order by a field which is a Navigation Property to an Entity - Linq to Entity

I've got a scenario where I will need to order by on a column which is a navigation property for the Users entity inside my EF model.
The entities:
Users --> Countries 1:n relationship
A simple SQL query would be as follows:
SELECT UserId, u.Name, c.Name
FROM users u join countries c on u.CountryId = c.CountryId
ORDER BY c.Name asc;
So then I tried to replicate the above SQL query using Linq to Entities as follows - (Lazy Loading is enabled)
entities.users.OrderBy(field => field.country.Name).ToList();
But this query does not return my countries sorted by their name as the native SQL query above does.
However I continued a bit more and did the following:
var enumeratedUsers = entities.users.AsEnumerable();
users = enumeratedUsers.OrderBy(fields => fields.country.Name).ToList();
But ordering on the enumeratedUser object for about 50 records took approx. 7seconds
Is there a better way how to omit the Enumerable and without returning an anonymous type?
Thanks
EDIT
I just forgot to say that the EF provider is a MySQL one not a MS SQL. In fact I just tried the same query on a replicated database in MS SQL and the query works fine i.e. the country name is ordered correctly, so it looks like I have no other option apart from getting the result set from MySQL and execute the order by from the memory on the enumerable object
var enumeratedUsers = entities.users.AsEnumerable();
users = enumeratedUsers.OrderBy(fields => fields.country.Name).ToList();
This is LINQ to Objects not LINQ to Entities.
Above Order By clause will call OrderBy defined in Enumerable
That is ordering will be done in memory. Hence it will take long time
Edit
It looks like a MySQL related issue
You may try something like this.
var users = from user in entities.users
join country in entities.Country on user.CountryId equals country.Id
orderby country.Name
select user;
entities.users.OrderBy(field => field.country.Name).ToList();
But this query does not return my countries sorted by their name as the native
SQL query above does.
Yes, it does not return Countries but only Users sorted by the name of country.
When this query is executed, the following sql is sent to DB.
SELECT u.*
FROM users u join countries c on u.CountryId = c.CountryId
ORDER BY c.Name asc;
As you can see, the result does not include any fields of countries. As you mentioned the lazy loading, countires are loaded through it when needed. At this time, countries are ordered as the order you call it through the lazy loading. You can access countries through the Local property of a entity set.
This point tells you that if you want user sorted by the name of country and also countires sorted by the name, you need the eagerly loading as #Dennis mentioned like:
entities.users.Include["country"].OrderBy(field => field.country.Name).ToList();
This is converted to the following sql.
SELECT u.*, c.*
FROM users u join countries c on u.CountryId = c.CountryId
ORDER BY c.Name asc;
Have you tried using Include?
entities.users.Include["country"].OrderBy(field => field.country.Name).ToList();
SOLUTION
Since I had both columns named Name in both Countries and Users table MySQL Connector was generating this output when order by country.Name was executed:
SELECT `Extent1`.`Username`, `Extent1`.`Name`, `Extent1`.`Surname`, `Extent1`.`CountryId`
FROM `users` AS `Extent1` INNER JOIN `countries` AS `Extent2` ON `Extent1`.`CountryId` = `Extent2`.`CountryId`
ORDER BY `Name` ASC
therefore this will result in ordering on the users.Name rather countries.Name
However MySQL have release version 6.4.3 .NET connector which has resolved a bunch of issues one of them being:
We are also including some SQL generation improvements related to our entity framework provider. Source: http://forums.mysql.com/read.php?3,425992
Thank you for all your input. I tried to be clear as much as possible to help others which might encounter my same issue.

Categories

Resources