How to avoid SQL N+1 issue using Fluent NHibernate/Nhibernate & Automapping

nabeelfarid's Avatar

nabeelfarid

29 Jun, 2010 04:51 PM via web

Hi guys,

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.
I'm not sure this is the correct way to go about it. However, the strange thing is that for some reason the returned IList.Count from the query is equal to the number of rows of the query. Every node in the list has some weird Cartesian product mess in the child collections with multiple instances of the same entity.

So e.g. if I have 1000 rows in the Node table,all of them having a reference to one entity type row in entity type table. The entity type row has two properties in the Property table, the IList returned by the query has nearly 2000 Nodes(duplicates), and the entity type object in each node has nearly 2000 properties(duplicates) and so on

In short I would like to resolve SQL N+1 issue here and I don't want NHiberneate to load duplicate nodes and duplicate child objects within them.

Thanks
Nabeel

  1. 2 Posted by nabeelfarid on 30 Jun, 2010 11:38 AM

    nabeelfarid's Avatar

    On google I found out about DistinctRootEntityResultTransformer but that only resolve the issue for the Root objects. I am still getting duplicates in the child collections. Every root object in the returned list has some weird Cartesian product mess in the child collections with multiple instances of the same entity. Any idea?

    Awaiting
    Nabeel

  2. 3 Posted by nabeelfarid on 30 Jun, 2010 03:30 PM

    nabeelfarid's Avatar

    I think I have found the solution but I would like to know if its the
    correct one.

    The child collections (EType.Properties, Etype.Properties.ListValues)
    inside root object (Node) are IList. And i read in the documentation
    that IList can contain duplicates, so if i change IList to ISet/
    ICollection, then the query does not load duplicate instances within
    the child collections.

    But this solution requires alot of refactoring. I would like to know
    if there is a way to achieve the same using IList for child
    collections?

    Awaiting,
    Nabeel

  3. 4 Posted by nabeelfarid on 01 Jul, 2010 02:02 PM

    nabeelfarid's Avatar

    Could anyone please reflect some light on this issue. Atleast let me know if y solution is correct ?

    Awaiting
    Nabeel

  4. Support Staff 5 Posted by Paul Batum on 05 Jul, 2010 12:17 PM

    Paul Batum's Avatar

    Have you tried asking on the nhibernate
    users<http://groups.google.com/group/nhusers/> mailing
    list? Your question is more about NH usage than FNH mappings.

  5. 6 Posted by nabeelfarid on 06 Jul, 2010 09:49 AM

    nabeelfarid's Avatar

    Hi Paul,

    Yes I have alraedy posted the same question on NHibernate forum as well as Stack Overflow but I am still not sure if my solution is the right one

    http://groups.google.com/group/nhusers/browse_thread/thread/59155d0...

    http://stackoverflow.com/questions/3142845/eager-loading-using-flue...

    Regards
    Nabeel

Reply to this discussion

Preview Comments are parsed with Markdown. Help with syntax

Attached Files

    You can attach files up to 10MB

    What is fifteen divided by three?