NHibernate和Entity Framework中延迟加载的区别

时间:2011-09-24 17:22:47

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

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

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

我的订单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);
}

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

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

有趣的是我的NHibernate映射(它是一个'产品 - 如下所示),似乎表现为'非常懒惰'的方式,即使我的映射被配置为只是懒惰:

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

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

有人可以评论这是否正确吗?

2 个答案:

答案 0 :(得分:1)

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

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

它基于延迟加载的实现方式。尽管您要在集合上使用操作,但每次访问属性/集合本身都会触发延迟加载。您将不得不完全避免构建延迟加载以完全避免它。 Here是如何为Count方法实现它的示例 - 您可以考虑使用其他方法的类似方法。

答案 1 :(得分:1)

  

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

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

  

lazy =“extra”的集合 - Lazy extra意味着NHibernate适应   您可能在集合上运行的操作。那   意味着blog.Posts.Count不会强制加载整个   收集,而是从帖子创建“选择计数(*)   其中BlogId = 1“语句,而blog.Posts.Contains()将   同样导致单个查询而不是付出代价   将整个集合加载到内存中。

向未初始化的延迟集合添加新项目将不会加载该集合。它不必被映射为额外的懒惰。看一下这个article

  

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