Search code examples
nhibernatenhibernate-criteria

loading multi-level child entities eagerly in NHibernate cause duplication problem


i have a Model class which contains some images and some features :

public class Model
{
    public int ModelId { get; set; }
    public string ModelName { get; set; }
    public virtual IList<Feature> ModelFeatures { get; set; }
    public virtual IList<ModelImage> ModelImages { get; set; }
}

public class ModelImage
{
    public virtual int ModelImageId { get; set; }
    public virtual Model Model { get; set; }
    public virtual Resource Image { get; set; }
    public virtual int DisplayOrder { get; set; }
}

public class Feature
{
    public virtual int FeatureId { get; set; }
    public virtual string Title { get; set; }
    public virtual string Text { get; set; }
}

now i want to load ModelImages and Features of a Model eagerly, i'm using :

item = session.CreateCriteria<Model>()
       .Add(NHibernate.Criterion.Expression.Where<Model>(o => o.ModelId == id))
       .SetFetchMode("ModelImages", NHibernate.FetchMode.Eager)
       .SetFetchMode("ModelImages.Image", NHibernate.FetchMode.Eager)
       .SetFetchMode("ModelFeatures", NHibernate.FetchMode.Eager)
       .SetResultTransformer(NHibernate.Transform.Transformers.DistinctRootEntity)
       .UniqueResult<Model>();

but the result contain duplicate ModelImage and ModelFeatures, how could i apply a result transformer such as DistinctRoot to these child collections ?

Thanks


Solution

  • I would split the query into two parts:

    item = session.CreateCriteria<Model>()
       .Add(NHibernate.Criterion.Expression.Where<Model>(o => o.ModelId == id))
       .SetFetchMode("ModelFeatures", NHibernate.FetchMode.Eager)
       .UniqueResult<Model>();
    
    session.CreateCriteria<Model>()
       .Add(NHibernate.Criterion.Expression.Where<Model>(o => o.ModelId == id))
       .SetFetchMode("ModelImages", NHibernate.FetchMode.Eager)
       .SetFetchMode("ModelImages.Image", NHibernate.FetchMode.Eager)
       .UniqueResult<Model>();
    

    The second query just continues to populate the collections of the "item" object returned from the first query, so there is no need to use the return value from the second query.

    If you want those two queries to be executed in a single round trip you can use Future() instead of UniqueResult() and then use item.Value to actually execute the queries.