NHibernate Lazy Loading Behaviour

lazy-evaluationlazy-loadingnhibernate

I was reading this article on Nhibernate Lazy Loading http://nhforge.org/wikis/howtonh/lazy-loading-eager-loading.aspx and it uses and example of a class structure like this:

Class Diagram

The article then shows the following code:

using (ISession session = SessionFactory.OpenSession())
{
    var fromDb = session.Get<Order>(_order.Id);
    int sum = 0;
    foreach (var line in fromDb.OrderLines)
    {
        // just some dummy code to force loading of order line
        sum += line.Amount;
    } 
}

It then goes on to talk about:

the n+1 select statements problem. If we access the order line items
after loading the order we generate a select statement for each line
item we access.

This is the behaviour I remebered of lazy loading, namely when I first get an order, the order lines collection is a proxy of a collection of order lines, then as I iterate through the order lines each one is loaded on demand.

However this is not the behaviour I am observing. What happens when I try this in my application is that when I get an order sure enough the collection of order lines is a proxy, but as soon as I access the first OrderLine using:

fromDb.OrderLines.First()

The entire collection is loaded into memory. This is a problem for me as the collection contains a lot of items and I only want to change one, but if I load all the items into memory and change one and try to save order I am obviously getting very poor performance.

So did the behaviour change since I this article was written? I am simply misunderstanding how lazy loading works? Or is there some way I can configure NHibernate to only load the items from the collection it needs?

Best Answer

"the n+1 select statements problem. If we access the order line items after loading the order we generate a select statement for each line item we access." is not corect. The Order lines are all loaded together because this is most of the time much more efficient. Select N+1 is mostly code like this:

var orders = session.QueryOver<Order>().List()

var usersWithOrders = orders.Select(o => o.User);

because you have 1 Select for the Orders and N Selects for each user (in reality only for distinct users because of session cache)

If you know you have large collections and only want to process some or need Count and Contains then there is <bag lazy="extra"> / HasMany(x => x.Lines).ExtraLazyLoad() which results in a collection proxy that issues queries for Count, Contains, this[] instead of loading it all.

Or you can session.QueryOver<OderLines>().Where(line => line.Order == order && ...) to get the specific lines you want to process

Related Topic