NHibernate lazy loading nested collections with futures to avoid N+1 problem

允我心安 提交于 2019-11-28 19:38:12

JoinAlias is another way to eagerly fetch related records, plus we can use it to dig another level deeper through Recommendations down to Images. We'll use LeftOuterJoin because we want to load the product even if it has no recommendations.

Product recommendationAlias = null;
Image imageAlias = null;

return CurrentSession.QueryOver<Product>()
    .JoinAlias(x => x.Recommendations, () => recommendationAlias, JoinType.LeftOuterJoin)
    .JoinAlias(() => recommendationAlias.Images, () => imageAlias, JoinType.LeftOuterJoin)
    .Where(x => x.Id == ID)
    .TransformUsing(Transformers.DistinctRootEntity)
    .SingleOrDefault();

When discussing eager fetching of multiple collections with NHibernate, you often hear people mention Cartesian products, but that's not a concern here. If however, you wished to load the following graph instead...

 Product -> Recommendations -> Images
         -> Images

... then Product.Recommendations.Images X Product.Images would form a Cartesian product that we should avoid. We could do so like this:

Product recommendationAlias = null;
Image imageAlias = null;

var productFuture = CurrentSession.QueryOver<Product>()
    .JoinAlias(x => x.Recommendations, () => recommendationAlias, JoinType.LeftOuterJoin)
    .JoinAlias(() => recommendationAlias.Images, () => imageAlias, JoinType.LeftOuterJoin)
    .Where(x => x.Id == ID)
    .TransformUsing(Transformers.DistinctRootEntity)
    .FutureValue();

var imagesFuture = CurrentSession.QueryOver<Product>()
    .Fetch(x => x.Images).Eager
    .Where(x => x.Id == ID)
    .TransformUsing(Transformers.DistinctRootEntity)
    .Future();

return productFuture.Value;

Force an eager load on the part of the graph you care about using the NHibernateUtil class.

 NHibernateUtil.Initialize(Product.Recommendations);

See the link below for further details.

http://nhforge.org/wikis/howtonh/lazy-loading-eager-loading.aspx

If all you want is avoiding the N+1 trouble, use Batch fetching of lazy loads instead of eager loading.

It removes N+1 issues while having a minimal impact on code: you just have to change a configuration parameter or adjust the mappings.

In configuration, set default_batch_fetch_size to some sensible value for your usual lazy-loads counts. 20 is usually a good value.

Or in mappings, set the batch-size attributes on classes (<class>) and collections (<set>, <bag>, ...) for controlling case by case the lazy-load batching.

This will configure your lazily loaded entities and collections of entities to not only load themselves, but also some others awaiting entities (of the same class) or collections of entities (same collections of other entities of the same class).

I have written a detailed explanation of it in this other answer.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!