NHibernate和实体框架延迟加载的区别



在使用实体框架和NHibernate使用POCO实体时,我做了以下观察,我发现有点不寻常:

我有两个POCO实体,一个'Order'和一个'Product'。两者之间存在多对多关系。当我向订单中添加产品时,我使用'FixUp'方法来确保关系的对立面也被更新,即'Orders'的产品集合也被更新。

我的订单POCO实体有以下方法来做'FixUp':

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

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

p.Order.Add(this);

对于Entity Framework,上面的行导致在数据库上执行select以返回产品'p'的所有订单。我不希望发生这种情况,因为我正在使用延迟加载,并且实际上不想访问产品的"Order"集合。我只是想给它加上一个顺序。

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

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

所以最终我的问题是,为什么实体框架生成额外的SQL语句?我不知道这两个框架的延迟加载实现有什么不同吗?

***编辑到原文一旦在集合上调用任何类型的方法,实体框架似乎不会以懒惰的方式行事。任何添加或计数(或可能对集合进行的任何其他操作)的尝试都会导致该集合被加载到内存中。

有趣的是我的NHibernate映射(这是一个'产品包-如下所示),似乎表现为'extra-lazy'时尚,即使我的映射被配置为只是懒惰:

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

我可以'Add'到集合而不将其加载到内存中。我认为调用'Count'将导致订单被加载,除非我将其配置为'extra-lazy'。

谁能评论一下这是否正确?

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

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

这取决于延迟加载是如何实现的。每次对属性/集合本身的访问都会触发延迟加载,而不管您想在集合上使用什么操作。您必须完全避免内置延迟加载才能完全避免它。下面是如何实现Count方法的示例-您可以考虑其他方法的类似方法。

我认为调用'Count'将导致订单被加载除非我将它配置为'extra-lazy'。

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

Collection with lazy= " extra " - lazy extra意味着NHibernate会适应到可能在集合上运行的操作。那意味着blog.Posts.Count不会强制整个加载集合,而是创建一个"select count(*) from Posts"where BlogId = 1 "语句,并且blog.Posts.Contains()将同样会导致单个查询,而不是支付的代价将整个集合加载到内存。

向未初始化的惰性集合添加新项将不会加载该集合。它不需要被映射为额外的lazy。看看这篇文章:

现在,对于映射为惰性加载的集合,Add()操作是允许,即使集合没有初始化(即集合只是作为代理)。将对象添加到集合时状态时,调用QueueAdd()方法,该方法将添加的对象存储在二次集合。一旦执行了延迟初始化,这次要收藏被合并到主要收藏中(我相信这是DelayedAddAll()方法执行此操作)。这可能很难调试因为延迟加载是透明地触发的,如果你只是触摸使用调试器收集(提供会话在

最新更新