NHibernate延迟加载带有期货的嵌套集合,以避免N + 1问题 [英] NHibernate lazy loading nested collections with futures to avoid N+1 problem

查看:68
本文介绍了NHibernate延迟加载带有期货的嵌套集合,以避免N + 1问题的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个看起来像这样的对象模型(伪代码):

I have an object model that looks like this (pseudo code):

class Product {
    public ISet<Product> Recommendations {get; set;}
    public ISet<Product> Recommenders {get; set;}
    public ISet<Image> Images {get; set; }
}

当我加载给定产品并想要显示其推荐的图像时,我遇到了N + 1问题. (建议是延迟加载的,然后循环调用每个建议的.Images属性.)

When I load a given product and want to display the images of its recommendations, I run into an N+1 problem. (The recommendations are lazy-loaded, then a loop calls the .Images property of each one.)

Product -> Recommendations -> Images

我想做的是急于加载图形的这一特定部分,但是我不知道该怎么做.我可以急于加载建议,但不能加载它们的图像.这是我一直在尝试的方法,但似乎不起作用:

What I want to do is eagerly load this particular part of the graph, but I can't figure out how to do it. I can load the recommendations eagerly, but not their images. This is what I have been trying, but it doesn't seem to work:

//get the IDs of the products that will be in the recommendations collection
var recommendedIDs = QueryOver.Of<Product>()
    .Inner.JoinQueryOver<Product>(p => p.Recommenders)
    .Where(r => r.Id == ID /*product we are currently loading*/)
    .Select(p => p.Id);

//products that are in the recommendations collection should load their 
//images eagerly
CurrentSession.QueryOver<Product>()
    .Fetch(p => p.Images).Eager
    .Where(Subqueries.WhereProperty<Product>(p => p.Id).In(recommendedIDs))
    .Future<Product>();

//load the current product
return CurrentSession.QueryOver<Product>()
    .Where(p => p.Id == ID);

使用QueryOver,完成此操作的最佳方法是什么?我不想一直渴望加载图像,只是在这种特定情况下.

Using QueryOver, what is the best way to accomplish this? I don't want to eagerly load images all the time, just in this particular scenario.

编辑:我已经改变了方法,虽然这并不是我的初衷,但确实避免了N + 1问题.我现在使用两个查询,一个查询产品,一个查询其推荐的图片.产品查询简单明了;这是图像查询:

EDIT: I have changed my approach, and while it's not exactly what I had in mind, it does avoid the N+1 problem. I am now using two queries, one for the product, and one for the images of it's recommendations. The product query is straight-forward; here is the image query:

//get the recommended product IDs; these will be used in
//a subquery for the images
var recommendedIDs = QueryOver.Of<Product>()
    .Inner.JoinQueryOver<Product>(p => p.Recommenders)
    .Where(r => r.Id == RecommendingProductID)
    .Select(p => p.Id);

//get the logo images for the recommended products and
//create a flattened object for the data
var recommendations = CurrentSession.QueryOver<Image>()
    .Fetch(i => i.Product).Eager
    /* filter the images down to only logos */
    .Where(i => i.Kind == ImageKind.Logo)
    .JoinQueryOver(i => i.Product)
    /* filter the products down to only recommendations */
    .Where(Subqueries.WhereProperty<Product>(p => p.Id).In(recommendedIDs))
    .List().Select(i => new ProductRecommendation {
        Description = i.Product.Description,
        ID = i.Product.Id,
        Name = i.Product.Name,
        ThumbnailPath = i.ThumbnailFile
    }).ToList();

return recommendations;

推荐答案

JoinAlias是热切获取相关记录的另一种方法,此外,我们可以使用它从Recommendations向下深入到Images.我们将使用LeftOuterJoin,因为即使没有任何建议,我们也要加载该产品.

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();

在讨论与NHibernate急切地获取多个集合时,您经常会听到人们提到笛卡尔积,但这并不是您所关心的.但是,如果您希望加载以下图形,则...

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

...然后是Product.Recommendations.Images X Product.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;

这篇关于NHibernate延迟加载带有期货的嵌套集合,以避免N + 1问题的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆