在使用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'。
有人可以评论这是否正确吗?
答案 0 :(得分:1)
这就是EF POCO模板及其FixUp
方法的行为方式。避免这种情况的唯一方法是:
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()方法)。这可能很难调试 因为如果你只是触摸,就会透明地触发延迟加载 使用调试器进行收集(提供会话连接在 那一刻),一切都得到了正确的初始化。