开发者

Difference between lazy loading in NHibernate and Entity Framework

While playing around with the Entity Framework and NHibernate using POCO entities, I made the following observation which I find a bit unusual:

I have two POCO entities, an 'Order' and a 'Product'. There is a many-to-many relationship between the two. When I add a product to an Order, I use a 'FixUp' method to ensure that the opposing side of the re开发者_StackOverflow中文版lationship is also updated i.e. - that the products collection of 'Orders' is also updated.

My Order POCO entity has the following method to do the 'FixUp':

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

While profiling this scenario with EFProf and NHProf, I observed that the Entity Framework generates one more SQL statement than NHibernate, the cause of which seems to be this line:

p.Order.Add(this);

With Entity Framework, the above line causes a select to be executed on the database to return all the orders for the product 'p'. I don't expect this to happen, since I'm using lazy loading and don't actually want to access the products 'Order' collection. I just want to add an order to it.

With NHibernate no attempt is made to load the products collection of orders unless I explicitly try to access it. For example, if I say:

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

So ultimately my question is, why does Entity Framework generate the extra SQL statement? Is there a difference in the implementation of lazy loading for the two frameworks that I'm not aware of?

***EDIT TO ORIGINAL It seems that Entity Framework doesn't behave in a lazy fashion once a method of any kind is called on the collection. Any attempt to add or count (or presumably any other operation on the collection) results in that collection being loaded into memory.

What's interesting is my NHibernate mapping (which is a bag of 'Products - shown below), appears to behave in an 'extra-lazy' fashion, even though my mapping is configured as just being lazy:

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

I can 'Add' to the collection without it being loaded into memory. I think that calling 'Count' will result in the orders being loaded unless I configure it as being 'extra-lazy'.

Can anyone comment on whether this is correct?


That is how EF POCO template and its FixUp methods behave. The only ways to avoid this are:

  • Removing FixUp methods from POCO template
  • Turn off lazy loading temporarily when assigning Product to Order

It is based on the way how lazy loading is implemented. Every access to property / collection itself triggers lazy loading despite of the operation you want to use on the collection. You will have to avoid build in lazy loading completely to fully avoid it. Here is example how to achieve it for Count method - you can think about similar approach for other methods.


I think that calling 'Count' will result in the orders being loaded unless I configure it as being 'extra-lazy'.

This is correct, calling Count as well as Contains will be optimized and will not load the whole collection in NHibernate. You may also find this EF vs NHibernate comparison interesting:

Collection with lazy=”extra” – Lazy extra means that NHibernate adapts to the operations that you might run on top of your collections. That means that blog.Posts.Count will not force a load of the entire collection, but rather would create a “select count(*) from Posts where BlogId = 1” statement, and that blog.Posts.Contains() will likewise result in a single query rather than paying the price of loading the entire collection to memory.

Adding a new item to an uninitialized lazy collection will not load the collection. It does not have to be mapped as extra lazy for this. Take a look at this article:

Now, on collections mapped as lazy-loading, Add() operations are allowed even if the collection is not initialized (i.e. the collection just acts as a proxy). When adding an object to a collection in this state, a QueueAdd() method is called that stores the added object in a secondary collection. Once a lazy initialization is performed, this secondary collection is merged into the main one (I believe it’s the DelayedAddAll() method that does this). This can be hard to debug because lazy load is transparently triggered if you just touch the collection with the debugger (providing the session is connected at that moment), and everything gets initialized properly.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜