【问题标题】:Eager Loading Using Fluent NHibernate/Nhibernate & Automapping使用 Fluent NHibernate/Nhibernate 和自动映射的渴望加载
【发布时间】:2010-06-29 16:48:32
【问题描述】:

我需要加载一个名为 Node 的复杂对象……嗯,它并不复杂……如下所示:-

Node 具有对 EntityType 的引用,该引用具有 一对多Property,而 Property 又具有一对多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()
    {
    }
}

我试图做的是一次加载所有子对象的 Node 对象。没有延迟加载。原因是我在数据库中有数千个 Node 对象,我必须使用 WCF 服务通过网络发送它们。我遇到了类 SQL N+1 问题。我正在使用 Fluent Nhibernate 和 Automapping,NHibernate Profiler 建议我使用 FetchMode.Eager 一次加载整个对象。我正在使用以下 qyuery

     Session.CreateCriteria(typeof (Node))
            .SetFetchMode( "Etype", FetchMode.Join )
            .SetFetchMode( "Etype.Properties", FetchMode.Join )
            .SetFetchMode( "Etype.Properties.ListValues", FetchMode.Join )

或者使用 NHibernate LINQ

        Session.Linq<NodeType>()
         .Expand( "Etype")
         .Expand( "Etype.Properties" )
         .Expand( "Etype.Properties.ListValues" )

当我运行上述任何查询时,它们都会生成一个包含所有左外连接的相同查询,这正是我所需要的。但是,由于某种原因,查询返回的 IList 没有被加载到对象中。实际上返回的Nodes计数等于查询的行数,因此Nodes对象是重复的。此外,每个Node中的属性都是重复的,Listvalues也是如此。

所以我想知道如何修改上述查询以返回所有唯一节点及其属性和列表值。

【问题讨论】:

  • 在谷歌上我发现了 DistinctRootEntityResultTransformer 但这只能解决 Root 对象的问题。我仍然在子集合中得到重复。返回列表中的每个根对象在具有相同实体的多个实例的子集合中都有一些奇怪的笛卡尔积混乱。任何的想法?等待纳比尔
  • 我想我已经找到了解决方案,但我想知道它是否正确。根对象(节点)内的子集合(EType.Properties、Etype.Properties.ListValues)是 IList。我在文档中读到 IList 可以包含重复项,因此如果我将 IList 更改为 ISet/ICollection,则查询不会在子集合中加载重复实例。但是这个解决方案需要大量的重构。我想知道是否有一种方法可以将 IList 用于子集合?等待着,纳比尔
  • 我有同样的问题(使用 Fetchmode.Eager)。我对 NHibernate 对此感到非常失望。我宁愿出错也不愿数据不正确。

标签: nhibernate fluent-nhibernate eager-loading automapping


【解决方案1】:

每个映射都必须关闭延迟加载

在节点映射中:

Map(x => x.EntityType).Not.LazyLoad();

在 EnityType 地图中:

Map(x => x.Properties).Not.LazyLoad();

等等……

另外,请参阅NHibernate Eager loading multi-level child objects 一次性急切加载

添加:

关于 Sql N+1 的附加信息:

http://nhprof.com/Learn/Alerts/SelectNPlusOne

【讨论】:

  • 感谢蒂姆的回复,好吧,我不想在映射中设置 .Not.LazyLoad() ,因为它会成为默认行为,并且在我的应用程序中,我有一个 wcf 服务需要将数据传递给客户端,我想在单个查询中一次加载所有数据以避免 SQL N+1 场景(nhprof.com/Learn/Alerts/SelectNPlusOne)。应用程序的其余部分不需要预先加载。那么知道如何解决这种情况吗?
  • 另外我的理解是 .Not.LazyLoad 并不能解决 SQL N+1 的问题。从 mNhibernate 分析器中,我注意到虽然它一次性加载了所有整个对象图,但它仍然会生成多个查询,即每个引用/hasmany 对象的查询,这是我不想要的,因为我有数百个节点entitytypes 和 properties,我不希望生成唯一的查询。纳比尔
  • 我以为你希望它以这种方式映射。我添加了一个指向另一个问题的链接,该问题显示了在特定实例中的急切加载。我认为这将帮助你产生一个连接。如果没有,您可以查看纠正存储过程并将其映射为命名查询。有关示例,请参阅 stackoverflow.com/questions/1637862/… 中的原始海报代码
  • 嗨,蒂姆,感谢您提供的信息。实际上我在生成查询时没有问题。生成的查询是正确的,由 nHibernate 分析器公开。奇怪的是,我的对象的返回列表有重复,并且返回列表中的每个根对象在具有相同实体的多个实例的子集合中都有一些奇怪的笛卡尔积混乱,但是在使用 DistinctRootEntityResultTransformer 并用 ICollection 而不是 IList 替换子集合之后,它按我的预期工作。但我想知道它是否是正确的解决方案?
【解决方案2】:

我自己想办法。关键是使用 SetResultTransformer() 传递 DistinctRootEntityResultTransformer 的对象作为参数。所以查询现在如下所示

Session.CreateCriteria(typeof (Node))
   .SetFetchMode( "Etype", FetchMode.Join )
   .SetFetchMode( "Etype.Properties", FetchMode.Join )
   .SetFetchMode( "Etype.Properties.ListValues", FetchMode.Join )
   .SetResultTransformer(new DistinctRootEntityResultTransformer());

我通过这些链接找到了我的问题的答案:

http://www.mailinglistarchive.com/html/nhusers@googlegroups.com/2010-05/msg00512.html

http://ayende.com/Blog/archive/2010/01/16/eagerly-loading-entity-associations-efficiently-with-nhibernate.aspx

【讨论】:

  • +1,但哇,这很难看。这看起来真是一团糟——我对 Nibernate 没有印象。为什么我们需要转换结果? NH 似乎没有做好它的工作。
【解决方案3】:

我最终得到了这样的结果:

HasMany(x => x.YourList).KeyColumn("ColumnName").Inverse().Not.LazyLoad().Fetch.Join()

只要确保像这样选择您的实体,以避免由于加入而导致重复:

session.CreateCriteria(typeof(T)).SetResultTransformer(Transformers.DistinctRootEntity).List<T>();

【讨论】:

  • 你也可以参考文献吗?
【解决方案4】:

带有 DistinctRootEntityResultTransformer 的 SetResultTransformer 仅适用于 Main 对象,但 IList 集合将成倍增加。

【讨论】:

  • 你将如何使用 ISet 或 ICollection?
猜你喜欢
  • 1970-01-01
  • 2011-04-10
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-01-11
  • 2012-10-19
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多