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?
Related
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.
Here is what I am trying:
IQueryable query = this.MyRepository.GetShippingCollection();
IList<SomeListType> myList = query.Where(x => x.Settings
.Where(y => y.SelectorID.Equals(5))
.Count() > 0)
.OrderBy(x => x.Order)
.ToList();
Produces this error:
could not resolve property: Settings.ID
If I do it this way it works, but causes over 3,000 queries on my SQL Server:
IList<SomeListType> myList = this.MyRepository.GetShippingCollection().ToList();
myList = myList.Where(x => x.Settings
.Where(y => y.SelectorID.Equals(5))
.Count() > 0)
.OrderBy(x => x.Order)
.ToList();
I know the solution resides within using a "Join".
I have been looking at examples for the last couple hours and can only find Join examples within the Mapping file. I am also finding examples for "ICriteria".
I don't want to have to create seporate entries for all my complex queries in the mapping file so the join within that file will not work.
Since I am using Fluent NHibernate, I am not using "ICriteria". I am using "IQueryable". How do you join multiple tables within "IQueryable"?
Any help with this would be greatly appreciated.
Thanks in advance.
If the second query is executing 3,000 queries, it is almost certainly lazy-loading the Settings collection. As you iterate over the list, you access this collection, and each time NHibernate goes back to the database to fetch it. Try setting the fetch mode for the Settings property to eager load in the mapping.
Beyond that, the LINQ provider could be an issue. What version of NHibernate are you using? The 2.x LINQ provider has some real limitations. It has been reimplemented in the 3.0 trunk, but you'll have to download and compile it from the source.
By the way, ICriteria vs IQueryable is not related to Fluent NHibernate. Criteria API and LINQ are two providers through which you can create queries. Fluent NHibernate is an alternative way to perform configuration.
How can I eagerly fetch a collection, but just the N first items?
Using this code works, but is there an 'official way' to achieve that?
public Gallery GetById(int id)
{
var session = GetSession();
var criteria = session.CreateCriteria<Gallery>()
.Add(Expression.Eq("Id", id))
.SetFetchMode("Pictures", FetchMode.Eager)
.CreateAlias("Pictures", "p")
.SetFirstResult(0)
.SetMaxResults(24)
;
return criteria.UniqueResult<Gallery>();
}
In this case, I'm bounding the results of Gallery, which is anyway unique result, but I want to bound the results of Pictures.
Your code works correctly, and is perfectly acceptable. If you want to always eagerly fetch, you can set it as such in your table mapping configuration (HBM, Fluent, or with whatever solution you use), and then explicitly tell it not to for the cases where you don't want to eagerly fetch. Both ways work fine and are acceptable. Use whichever is more convenient or safe for your project needs and team's coding style.
I have a product table that has a many-to-many relation to itself (using a two-column many-to-many table) and I have set it up in Fluent NHibernate with the following code:
public class ProductConfiguration : ClassMap<Product>
{
public ProductConfiguration()
{
Table("Product");
Id(p => p.Id).GeneratedBy.Guid();
Map(p => p.Name).Not.Nullable().Length(254);
Map(p => p.Description).Not.Nullable().Length(1000);
Map(p => p.CreatedAt).Not.Nullable();
HasManyToMany(p => p.CrossSell)
.Table("ProductCrossSell")
.ParentKeyColumn("Id")
.ChildKeyColumn("ProductId");
}
}
My MVC application has two pages that uses this setup:
Index - Uses a generic repository GetAll method to display all products.
Detail - Uses a generic repository GetById method to display one product and any related cross sell products setup in the many-to-many realation.
It looks like NHibernate is set to LazyLoad the many-to-many by default so when I fire up the application and watch it in profiler I can see that it does LazyLoad the many-to-many with the following alert "Use of implicit transactions is discouraged".
How do I get rid of this alert? I couldn't find any information on how to wrap a LazyLoad inside a transaction to get rid the alert. Is it even possible?
Is there a way to not lazyload this by telling NHibernate that whenever I ask for GetById make sure to join the tables a get everything in one query? I tried using .Fetch.Join() in the many-to-many mapping but that also affected my GetAll query which now displays a joined result set as well which is incorrect.
What is the best apprach for this kind of simple scenario?
Thanks
The way to get rid of the warning is to access the object graph and fully populate the UI elements inside a single transaction.
Not by configuration. You can create an HQL query that eager fetches the association and use that query for a specific view. I would stick with lazy loading and not make that optimization unless needed. The HQL would be:
return session.CreateQuery("from ProductionConfiguration pc join fetch pc.CrossSell where pc.Id = ?")
.SetGuid(0, id)
.List<ProductConfiguration>();
All collections are lazily loaded in NHibernate by default.
You must be triggering loading with a call of some kind (maybe even with the debugger watches)
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.