Eager loading problem - nhibernate

I've a problem with this linq to nhibernate query
var listeShopping = (from cart in session.Query<Cart>()
.Fetch(cart => cart.ItemShopping)
.ThenFetch(item => item.Manufacturer)
select cart.ItemShopping).ToList<ItemShopping>();
When I launch it I've a strange error :
Query specified join fetching, but the owner of the fetched association was
not present in the select list [FromElement{explicit,not a collection join,
fetch join,fetch non-lazy properties,classAlias=_1,role=,tableName= (...)
I need the eager loading, how can I avoid that error ? If it can help, I'll mention that I use the cart table only like a inner join table. I just need to know the ItemShopping in the Cart.
Regards
Edit
I modified the code to make it readable in english. I corrected the error.
Edit 2
I found that method, it seems to work ... Can someone check it if I didn't make an error ?
var list = (from item in session.Query<ItemShopping>()
.Fetch(item => item.Manufacturer)
from cart in item.Cart
select item).ToList<ItemShopping>();

I don't think it's possible to handle this scenario with Linq. But it is with HQL:
var listeShopping = session.CreateQuery(#"
select item
from Cart cart
join cart.ItemShopping item
join fetch item.Manufacturer
")
.List<ItemShopping>();
Side note: eager fetching the Manufacturer this way is not necessarily the best performing approach. Consider using batch-size in the Manufacturer class instead.

Related

Direct implementation of EF Core many-to-many relationship unable to detect the related table

I've been following this tutorial on setting up a proper many-to-many relationship with EF Core. I've implemented what the author calls a direct relationship. Everything's been working fine until this example:
var books = context.Books.Tags.Select(t => t.TagId).ToList()
for which VS returns an error saying that DbSet does not contain a definition for Tags. I don't understand what's causing this, as the database is implemented just fine, and querying it with MSSQL works as expected. For example, I was able to do:
SELECT c.*
FROM Books o
JOIN BookTags ot ON ot.BooksBookId = o.TagId
JOIN Tags c ON ot.TagsTagId = c.TagId
WHERE o.BookId = 1
and get a list of Tags on Book with ID = 1. Ideally, I'd like to get the same result with EF Core, but I really don't understand what's broken here. Any help is greatly appreciated.
You cannot load navigation properties like that. To load books with navigation property, use:
var books = context.Books.Include(b => b.Tags).ToList();
To get the ids only, you can use:
var tagIds = context.Books.SelectMany(b => b.Tags).Select(t => t.TagId).ToList();

NHibernate Queryover - Is there any way of getting better SQL out of nHibernate when retrieving this object plus collections graph?

We have a situation where we are trying to retrieve a couple of levels deep for an object graph using QueryOver. So if our top level is ParentEntity and our children are ChildEntitysA, ChildEntitysB and ChildEntitysC then we have something like the following code to retrieve our graph.
var query = session.QueryOver<ParentEntity>(() => pAlias).Where(() => pAlias.Id == key).Future<ParentEntity>();
var queryA = session.QueryOver<ParentEntity>(() => pAlias).Left.JoinAlias(x => x.ChildEntitysA, () => caAlias).Where(() => pAlias.Id == key).Future<ParentEntity>();
var queryB = session.QueryOver<ParentEntity>(() => pAlias).Left.JoinAlias(x => x.ChildEntitysB, () => cbAlias).Where(() => pAlias.Id == key).Future<ParentEntity>();
var queryC = session.QueryOver<ParentEntity>(() => pAlias).Left.JoinAlias(x => x.ChildEntitysC, () => ccAlias).Where(() => pAlias.Id == key).Future<ParentEntity>();
return query.ToList();
This should generate 4 SQL statements in one single call to the database and brings back what we want. However, the SQL it generates does have some inefficiency as all of the child queries will contain in the SELECT statement all the columns of the parent as well as the child entity's columns, something akin to:
SELECT parent.col0, parent.col1, parent.col2, parent.col3, .... parent.col99
FROM parent WHERE .....
SELECT parent.col0, parent.col1, parent.col2, parent.col3, .... parent.col99, childA.col0, childA.col1, childA.col2 .... childA.col99
FROM parent LEFT OUTER JOIN childA ON ....
SELECT parent.col0, parent.col1, parent.col2, parent.col3, .... parent.col99, childB.col0, childB.col1, childB.col2 .... childB.col99
FROM parent LEFT OUTER JOIN childB ON ....
SELECT parent.col0, parent.col1, parent.col2, parent.col3, .... parent.col99, childC.col0, childC.col1, childC.col2 .... childC.col99
FROM parent LEFT OUTER JOIN childC ON ....
yet the query generated for the parent entity already contains all the necessary data! This does become a bit of an efficiency concern if we're returning multiples of the parent and the volume of data per child query rises accordingly (especially as the entities involved are quite large in terms of the number of underlying columns).
So, is there any way we can force nHibernate to not generate SQL to return all the parent entity's values for every single part of the query and just limit it to returning minimal key columns only in the SQL? Is Queryover even the optimal API for this situation?
The way I would suggest, is to use full power of NHibernate features in these combinations:
The first is a complex query.
The second is to profit from lazy loading and batch-size setting
So, in the first case we are about to create one and only one QueryOver (maybe including some subqueries)
It must contain projections, that's essential. it can include (many) Left, Inner joins, can be deeply filtered... We will then get the narrowed SELECT statement, containing only required fields. This results in one SQL query, which must be converted into some (unmapped) DTO object
The Projections and DTO means, that at the end we have to use ResultTransformer, which will convert all coming selected fields into DTO object properties.
In the second case, we are trying to profit from lazy behaviour and batch-size mapping. It could look like this:
1) class/ entity
<class name="ParentEntity" batch-size="25" lazy="true">
...
2) collection
<bag name="Children" lazy="true" batch-size="25"
...
See more in documentation: 19.1.5. Using batch fetching
What we gain here is, that we can create soft queries returning only light objects (these which were mapped). This is the first Select. Then all the properties (many-to-one, one-to-many) are loaded in batches - but only if they are accessed, needed.
This leads to more than 1 SELECT clause, but also there is no inflation of SELECT clauses like 1 + N. If NHibernate will find out, that some of the required properties is already loaded in the session... it won't fire the SELECT any more.
Summary: both approaches are really the way. Try to play arround to find out in which situation you get more from the first or the second. BUT the mapping with lazy="true" and batch-size="25" should be used anyway
Some more links about batch-size:
How to Eager Load Associations without duplication in NHibernate?
NHibernate QueryOver with Fetch resulting multiple sql queries and db hits
Is this the right way to eager load child collections in NHibernate

NHibernate FetchMode Cartesian Product

In my object graph, VendorServiceRateChange has a lazy loaded property IList<VendorService> VendorServiceList and the VendorService has a lazy loaded property IList<ClientService>.
When I run the following code I get a Cartesian product between my VendorServiceList and my ClientServiceList.
queueList = session
.CreateCriteria<VendorRateChange>()
.Add(Restrictions.IsNull("ProcessedDate"))
.Add(Restrictions.Le("EffectiveDate", DateTime.Now))
.SetFetchMode("VendorServiceList", FetchMode.Join)
.SetFetchMode("VendorServiceList.Vendor", FetchMode.Join)
.SetFetchMode("VendorServiceList.CityService", FetchMode.Join)
.SetFetchMode("VendorServiceList.ClientServices", FetchMode.Join)
.SetResultTransformer(new DistinctRootEntityResultTransformer())
.AddOrder(new Order("Audit.CreatedDate", true))
.List<VendorRateChange>();
Is there a way to structure this query using Criteria or DetachedCriteria that would not result in a Cartesian product as my VendorServiceList? I do not want to have to resort to commenting out the fetch on "VendorServiceList.ClientServices" and looping through with an Initialize call after the initial query has come back.
Thanks in advance.
Are you sure it is a cartesian product, or you just receive "duplicated" rows in the retuned list ?
If so, before .List(), try to call .SetResultTransformer(Transformers.DistinctRootEntity), because by eager fetching the associated collection you receive a list containing duplicates of the same entity.
You can have a single FetchMode.Join before you start creating cartesian products. What if you switch others to FetchMode.Select or something else?

Fluent nHibernate Selective loading for collections

I was just wondering whether when loading an entity which contains a collection e.g. a Post which may contain 0 -> n Comments if you can define how many comments to return.
At the moment I have this:
public IList<Post> GetNPostsWithNCommentsAndCreator(int numOfPosts, int numOfComments)
{
var posts = Session.Query<Post>().OrderByDescending(x => x.CreationDateTime)
.Take(numOfPosts)
.Fetch(z => z.Comments)
.Fetch(z => z.Creator).ToList();
ReleaseCurrentSession();
return posts;
}
Is there a way of adding a Skip and Take to Comments to allow a kind of paging functionality on the collection so you don't end up loading lots of things you don't need.
I'm aware of lazy loading but I don't really want to use it, I'm using the MVC pattern and want my object to return from the repositories loaded so I can then cache them. I don't really want my views causing select statements.
Is the only real way around this is to not perform a fetch on comments but to perform a separate Select on Comments to Order By Created Date Time and then Select the top 5 for example and then place the returned result into the Post object?
Any thoughts / links on this would be appreciated.
Thanks,
Jon
A fetch simple does a left-outer join on the associated table so that it can hydrate the collection entities with data. What you are looking to do will require a separate query on the specific entities. From there you can use any number of constructs to limit your result set (skip/take, setmaxresults, etc)

Eager loading child collection with NHibernate

I want to load root entities and eager load all it's child collection and aggregate members.
Have been trying to use the SetFetchMode in FluentNHibernate, but I am getting duplicates in one of the child collection since I have a depth of 3 levels. DistinctRootEntityResultTransformer unfortunately only removes the root duplications.
return Session.CreateInvoiceBaseCriteria(query, archived)
.AddOrder(new Order(query.Order, query.OrderType == OrderType.ASC))
.SetFetchMode("States", FetchMode.Eager)
.SetFetchMode("Attestations", FetchMode.Eager)
.SetFetchMode("AttestationRequests", FetchMode.Eager)
.SetFetchMode("AttestationRequests.Reminders", FetchMode.Eager)
.SetResultTransformer(new DistinctRootEntityResultTransformer())
.List<Invoice>();
Could I use multi queries or something similar to archive this?
Furthermore, wouldn't this approach result in unnecessarily huge result sets from the database?
Any suggestions?
Found a solution but it isn't pretty. First I go and find all the Invoice IDs, then I use them in the multiquery and then at the end filtering the results through a HashedSet. Because of the large number of items sometimes i couldn't use the normalt Restriction.In and was forced to send it as a string.
Any suggested tweaks?
var criteria = Session.CreateInvoiceBaseCriteria(query, archived)
.SetProjection(Projections.Id());
var invoiceIds = criteria.List<int>();
if (invoiceIds.Count > 0)
{
var joinedIds = JoinIDs(criteria.List<int>()); // To many ids to send them as parameters.
var sql1 = string.Format("from Invoice i inner join fetch i.States where i.InvoiceID in ({0}) order by i.{1} {2}", joinedIds, query.Order, query.OrderType.ToString());
var sql2 = string.Format("from Invoice i inner join fetch i.AttestationRequests where i.InvoiceID in ({0})", joinedIds);
var sql3 = string.Format("from Invoice i inner join fetch i.Attestations where i.InvoiceID in ({0})", joinedIds);
var invoiceQuery = Session.CreateMultiQuery()
.Add(sql1)
.Add(sql2)
.Add(sql3);
var result = invoiceQuery.List()[0];
return new UniqueFilter<Invoice>((ICollection)result);
}
return new List<Invoice>();
To answer your question: yes, it results in huge resultsets.
I suggest to:
just naively write your queries without eager fetching
On certain places, put an eager fetch, but only one per query
if you really get performance problems which you can't solve with indexes or by enhance queries and mapping strategies, use your solution with the multiple queries.
While it might not be exactly what you are looking for, I would recommend looking at this article:
Eager loading aggregate with many child collections
If you look around the rest of that site you will find even more posts that discuss eager loading and other great nHibernate stuff.