14

私はこのようなオブジェクトモデルを持っています(擬似コード):

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

特定の製品をロードして、その推奨の画像を表示したい場合、N+1の問題が発生します。(推奨事項は遅延ロードされ、ループがそれぞれの.Imagesプロパティを呼び出します。)

Product -> Recommendations -> Images

私がやりたいのは、グラフのこの特定の部分を熱心にロードすることですが、それを行う方法がわかりません。推奨事項を熱心に読み込むことはできますが、画像を読み込むことはできません。これは私が試したことですが、うまくいかないようです:

//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を使用して、これを達成するための最良の方法は何ですか?この特定のシナリオだけで、常に画像を熱心にロードしたくありません。


編集:私は私のアプローチを変更しました、そしてそれは私が考えていたものとは正確には異なりますが、それはN+1の問題を回避します。現在、2つのクエリを使用しています。1つは製品用で、もう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;
4

3 に答える 3

17

JoinAlias関連するレコードを熱心にフェッチするもう1つの方法です。さらに、これを使用して、までさらに深く別のレベルを掘り下げることができRecommendationsますImagesLeftOuterJoin推奨事項がなくてもロードしたいので使用します。

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と複数のコレクションの熱心なフェッチについて話し合うとき、人々がデカルト積について言及するのをよく耳にしますが、それはここでは問題ではありません。ただし、代わりに次のグラフをロードしたい場合は...

 Product -> Recommendations -> Images
         -> Images

...次に、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;
于 2011-08-12T04:40:06.540 に答える
3

NHibernateUtilクラスを使用して、グラフの関心のある部分に熱心な負荷をかけます。

 NHibernateUtil.Initialize(Product.Recommendations);

詳細については、以下のリンクを参照してください。

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

于 2011-04-11T16:17:16.060 に答える
1

N + 1の問題を回避するだけの場合は、積極的な読み込みではなく、遅延読み込みのバッチフェッチを使用します。

コードへの影響を最小限に抑えながら、N + 1の問題を取り除きます。構成パラメーターを変更するか、マッピングを調整するだけです。

構成でdefault_batch_fetch_sizeは、通常の遅延読み込みカウントに適切な値を設定します。20通常は良い値です。

または、マッピングで、レイジーロードバッチ処理をケースバイケースで制御するために、batch-sizeクラス(<class>)とコレクション(<set>、、 ...)に属性を設定します。<bag>

これにより、遅延ロードされたエンティティとエンティティのコレクションが、自分自身だけでなく、(同じクラスの)エンティティまたはエンティティのコレクション(同じクラスの他のエンティティの同じコレクション)を待機している他のいくつかのエンティティもロードするように構成されます。

私はこの他の答えにそれの詳細な説明を書きました。

于 2017-05-11T16:22:38.583 に答える