I have a requirement to load a complex object called Node...well its not that complex...it looks like follows:-
A Node has a reference to EntityType which has a one to many with Property which in turn has a one to many with PorpertyListValue
public class Node
{
public virtual int Id
{
get;
set;
}
public virtual string Name
{
get;
set;
}
public virtual EntityType Etype
{
get;
set;
}
}
public class EntityType
{
public virtual int Id
{
get;
set;
}
public virtual string Name
{
get;
set;
}
public virtual IList<Property> Properties
{
get;
protected set;
}
public EntityType()
{
Properties = new List<Property>();
}
}
public class Property
{
public virtual int Id
{
get;
set;
}
public virtual string Name
{
get;
set;
}
public virtual EntityType EntityType
{
get;
set;
}
public virtual IList<PropertyListValue> ListValues
{
get;
protected set;
}
public virtual string DefaultValue
{
get;
set;
}
public Property()
{
ListValues = new List<PropertyListValue>();
}
}
public class PropertyListValue
{
public virtual int Id
{
get;
set;
}
public virtual Property Property
{
get;
set;
}
public virtual string Value
{
get;
set;
}
protected PropertyListValue()
{
}
}
What I a trying to do is load the Node object with all the child objects all at once. No Lazy load. The reason is I have thousands of Node objects in the database and I have to send them over the wire using WCF Service.I ran into the classes SQL N+ 1 problem. I am using Fluent Nhibernate with Automapping and NHibernate Profiler suggested me to use FetchMode.Eager to load the whole objects at once. I am using the following qyuery
Session.CreateCriteria(typeof (Node))
.SetFetchMode( "Etype", FetchMode.Join )
.SetFetchMode( "Etype.Properties", FetchMode.Join )
.SetFetchMode( "Etype.Properties.ListValues", FetchMode.Join )
OR using NHibernate LINQ
Session.Linq<NodeType>()
.Expand( "Etype")
.Expand( "Etype.Properties" )
.Expand( "Etype.Properties.ListValues" )
When I run any of the above query, they both generate one same single query with all the left outer joins, which is what I need. However, for some reason the return IList from the query is not being loaded property into the objects. Infact the returned Nodes count is equal to the number of rows of the query, so the Nodes objects are repeated.Moreover, the properties within each Node are repeated, and so do the Listvalues.
So I would like to know how to modify the above query to return all unique Nodes with the properties and list values within them.
Fluent NHibernate offers an alternative to NHibernate's standard XML mapping files. Rather than writing XML documents, you write mappings in strongly typed C# code. This allows for easy refactoring, improved readability and more concise code.
A more thorough answer: Lazy loading is the default in NHibernate. So if you are not doing anything to circumvent it, you are going to use lazy loading. With lazy loading you can use nested objects (with many relationships to other tables) and it is going to work fine. They are loaded from the database as needed.
Fetching strategies. A fetching strategy is the strategy NHibernate will use for retrieving associated objects if the application needs to navigate the association. Fetch strategies may be declared in the O/R mapping metadata, or overridden by a particular HQL or Criteria query.
each mapping has to have lazy loading off
in Node Map:
Map(x => x.EntityType).Not.LazyLoad();
in EnityType Map:
Map(x => x.Properties).Not.LazyLoad();
and so on...
Also, see NHibernate Eager loading multi-level child objects for one time eager loading
Added:
Additional info on Sql N+1:
http://nhprof.com/Learn/Alerts/SelectNPlusOne
I figure it out myself. The key is to use SetResultTransformer() passing an object of DistinctRootEntityResultTransformer as a parameter. So the query now looks like as follows
Session.CreateCriteria(typeof (Node))
.SetFetchMode( "Etype", FetchMode.Join )
.SetFetchMode( "Etype.Properties", FetchMode.Join )
.SetFetchMode( "Etype.Properties.ListValues", FetchMode.Join )
.SetResultTransformer(new DistinctRootEntityResultTransformer());
I found the answer to my questions through these links:
http://www.mailinglistarchive.com/html/[email protected]/2010-05/msg00512.html
http://ayende.com/Blog/archive/2010/01/16/eagerly-loading-entity-associations-efficiently-with-nhibernate.aspx
I ended up with something like this:
HasMany(x => x.YourList).KeyColumn("ColumnName").Inverse().Not.LazyLoad().Fetch.Join()
Just make sure to select your entity like this, to avoid duplication due to the join:
session.CreateCriteria(typeof(T)).SetResultTransformer(Transformers.DistinctRootEntity).List<T>();
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With