NHibernate延迟加载具有未来的嵌套集合,以避免N+;1问题

NHibernate延迟加载具有未来的嵌套集合,以避免N+;1问题,nhibernate,queryover,Nhibernate,Queryover,我有一个如下的对象模型(伪代码): 我想做的是急切地加载图表的这一特定部分,但我不知道怎么做。我可以急切地加载推荐,但不能加载它们的图像。这是我一直在尝试的,但似乎不起作用: //get the IDs of the products that will be in the recommendations collection var recommendedIDs = QueryOver.Of<Product>() .Inner.JoinQueryOver<Produc

我有一个如下的对象模型(伪代码):

我想做的是急切地加载图表的这一特定部分,但我不知道怎么做。我可以急切地加载推荐,但不能加载它们的图像。这是我一直在尝试的,但似乎不起作用:

//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);
//获取将在“建议”集合中的产品的ID
var recommendedds=QueryOver.Of()
.internal.JoinQueryOver(p=>p.Recommenders)
.Where(r=>r.Id==Id/*我们当前正在加载的产品*/)
.选择(p=>p.Id);
//“建议”集合中的产品应加载其
//热切地想象
CurrentSession.QueryOver()
.Fetch(p=>p.Images)
.Where(子查询.WhereProperty(p=>p.Id).In(推荐的dDS))
.Future();
//加载当前产品
返回CurrentSession.QueryOver()
其中(p=>p.Id==Id);
使用QueryOver,实现这一点的最佳方法是什么?我不想总是急切地加载图像,只是在这个特定场景中


编辑:我改变了我的方法,虽然这不完全是我所想的,但它确实避免了N+1问题。我现在使用两个查询,一个用于产品,另一个用于其推荐的图像。产品查询是直接的;以下是图像查询:

//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;
//获取推荐的产品ID;这些将用于
//图像的子查询
var recommendedds=QueryOver.Of()
.internal.JoinQueryOver(p=>p.Recommenders)
.其中(r=>r.Id==推荐产品Id)
.选择(p=>p.Id);
//获取推荐产品的徽标图像,以及
//为数据创建展开对象
var建议=CurrentSession.QueryOver()
.Fetch(i=>i.Product)
/*将图像过滤到只包含徽标*/
.Where(i=>i.Kind==ImageKind.Logo)
.JoinQueryOver(i=>i.Product)
/*将产品筛选为仅推荐的产品*/
.Where(子查询.WhereProperty(p=>p.Id).In(推荐的dDS))
.List().Select(i=>new ProductRecommension{
Description=i.Product.Description,
ID=i.Product.ID,
Name=i.Product.Name,
ThumbnailPath=i.ThumbnailFile
}).ToList();
返回建议;

使用NHibernateUtil类在您关心的图形部分强制加载一个渴望加载

 NHibernateUtil.Initialize(Product.Recommendations);
有关更多详细信息,请参阅下面的链接


JoinAlias
是热切获取相关记录的另一种方式,此外,我们还可以使用它通过
推荐
深入到
图像
。我们将使用
LeftOuterJoin
,因为我们希望加载产品,即使它没有建议

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();
。。。然后Product.Recommendations.Images X Product.Images将形成我们应该避免的笛卡尔积。我们可以这样做:

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;
产品推荐别名=null;
图像imageAlias=null;
var productFuture=CurrentSession.QueryOver()
.JoinAlias(x=>x.Recommendations,()=>recommendationAlias,JoinType.LeftOuterJoin)
.JoinAlias(()=>recommendationAlias.Images,()=>imageAlias,JoinType.LeftOuterJoin)
.其中(x=>x.Id==Id)
.变压器使用(变压器.距离)
.未来价值();
var imagesFuture=CurrentSession.QueryOver()
.Fetch(x=>x.Images)。急切
.其中(x=>x.Id==Id)
.变压器使用(变压器.距离)
.Future();
回报产品未来价值;

如果您只想避免N+1问题,请使用延迟加载而不是快速加载

它消除了N+1问题,同时对代码的影响最小:您只需更改配置参数或调整映射即可

在配置中,将
default\u batch\u fetch\u size
设置为一些合理的值,以用于您通常的延迟加载计数<代码>20通常是一个很好的值

或者在映射中,设置类(
)和集合(
,…)上的
批大小
属性,以逐案控制延迟加载批处理

这将配置延迟加载的实体和实体集合,以便不仅加载它们自己,还加载一些等待实体(属于同一类)或实体集合(属于同一类的其他实体的相同集合)


我已经在中写了详细的解释。

没问题!很高兴我能帮忙。
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();
 Product -> Recommendations -> Images
         -> Images
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;