【问题标题】:Difference between lazy loading in NHibernate and Entity FrameworkNHibernate 和实体框架中延迟加载的区别
【发布时间】:2011-11-24 07:57:43
【问题描述】:

在使用 POCO 实体玩弄 Entity Framework 和 NHibernate 时,我做了以下观察,我觉得这有点不寻常:

我有两个 POCO 实体,一个“订单”和一个“产品”。两者之间存在多对多的关系。当我将产品添加到订单时,我使用“FixUp”方法来确保关系的另一方也被更新,即 - “订单”的产品集合也被更新。

我的订单 POCO 实体具有以下方法来执行“修复”:

private void FixupProducts(object sender, NotifyCollectionChangedEventArgs e)
{
    if(e.NewItems != null)
    {
        foreach(Product p in e.NewItems)
        {
            p.Order.Add(this);
        }
    }
}

在使用 EFProf 和 NHProf 分析此场景时,我观察到实体框架生成的 SQL 语句比 NHibernate 多,其原因似乎是这一行:

p.Order.Add(this);

使用实体框架,上面的行会导致在数据库上执行选择以返回产品“p”的所有订单。我不希望发生这种情况,因为我正在使用延迟加载并且实际上并不想访问产品“订单”集合。我只想给它添加一个订单。

使用 NHibernate 不会尝试加载订单的产品集合,除非我明确尝试访问它。例如,如果我说:

foreach(Order o in product.Orders)
{
    Console.WriteLine(o.Id);
}

所以最终我的问题是,为什么 Entity Framework 会生成额外的 SQL 语句?我不知道的两个框架的延迟加载实现是否有区别?

***编辑为原始 一旦在集合上调用任何类型的方法,Entity Framework 似乎不会以惰性方式运行。任何添加或计数的尝试(或可能对集合的任何其他操作)都会导致该集合被加载到内存中。

有趣的是我的 NHibernate 映射(它是一个“产品包 - 如下所示),尽管我的映射被配置为只是懒惰的,但它的行为似乎是一种“超懒惰”的方式:

<bag name="Products" cascade ="all"  table="OrderProduct" mutable="true" lazy="true">

我可以在不加载到内存的情况下“添加”到集合中。我认为调用 'Count' 会导致订单被加载,除非我将其配置为 'extra-lazy'。

谁能评论这是否正确?

【问题讨论】:

    标签: nhibernate entity-framework-4 lazy-loading


    【解决方案1】:

    这就是 EF POCO 模板及其FixUp 方法的行为方式。避免这种情况的唯一方法是:

    • 从 POCO 模板中删除 FixUp 方法
    • Product 分配给Order 时暂时关闭延迟加载

    它基于延迟加载的实现方式。尽管您想在集合上使用操作,但对属性/集合本身的每次访问都会触发延迟加载。您必须完全避免构建延迟加载以完全避免它。 HereCount 方法实现它的示例 - 您可以考虑其他方法的类似方法。

    【讨论】:

      【解决方案2】:

      我认为调用 'Count' 会导致订单被加载 除非我将其配置为“超级懒惰”。

      这是正确的,调用 Count 和 Contains 将被优化并且不会在 NHibernate 中加载整个集合。您可能还会发现这个EF vs NHibernate 比较很有趣:

      Collection with lazy=”extra” – Lazy extra 意味着 NHibernate 适应 到您可能在集合之上运行的操作。那 意味着 blog.Posts.Count 不会强制加载整个 集合,而是从 Posts 创建一个“select count(*) 其中 BlogId = 1” 声明,并且 blog.Posts.Contains() 将 同样会导致单个查询,而不是支付 将整个集合加载到内存中。

      向未初始化的惰性集合添加新项目不会加载该集合。它不必为此被映射为额外的惰性。看看这个article

      现在,在映射为延迟加载的集合上,Add() 操作是 即使集合未初始化(即集合 只是充当代理)。将对象添加到此集合时 状态,调用 QueueAdd() 方法将添加的对象存储在 二次收藏。一旦执行了延迟初始化,这 次要集合被合并到主要集合中(我相信它是 执行此操作的 DelayedAddAll() 方法)。这可能很难调试 因为如果你只是触摸延迟加载就会透明地触发 使用调试器收集(假设会话在 那一刻),一切都被正确初始化了。

      【讨论】:

      • “添加”具体怎么样?这似乎不会导致 NHibernate 加载整个集合。但是,如果我要在添加实体之前对集合进行检查,那么它会导致整个集合被加载。例如:If(!collection.Contains(item) { collection.add(item) }。我只是将集合配置为“懒惰”,而不是特别懒惰。
      猜你喜欢
      • 2011-03-14
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多