问题描述
我有一个看起来像这样的对象模型(伪代码):
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问题的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!