2017-01-25 6 views
1

私はEntity Frameworkを使用していますが、作業を繰り返すために必要なリストがあります。私はデータベース上のクエリから直接この作業を行うことができず、リストは静かにすることができるので、Parallel ForeachまたはAsParallelを使用することができます。遅延読み込みリストを使用する並列Foreach

問題は、メモリにToList()を使用してリストをロードしてからパラレル関数で実行しても、レイジーロードされたナビゲーションプロパティが破棄されるということです。私はこのような単純なもの(これは、この質問の目的のために多くを簡素化されました)実行しています

var quoteList = DbContext.Quotes.ToList(); 

List<QuotesItem> quoteViewItemList = new List<QuotesItem>(); 

quoteList.ForAll(quote => { 
    var quoteViewItem = new QuotesItem(); 
    quoteViewItem.YearEnding = quote.QuoteService.FirstOrDefault().Yearending; //is SOMETIMES null when it shouldn't be. 
    quoteViewItem.Manager quote.Client.Manager.Name; //IS sometimes null 
    quoteViewItem.... = ..... // More processing here 
    quoteViewItemList.Add(quoteViewItem); 
}); 

問題がQuoteServiceではNULLであるように思わ時にはときに、リスト内のNULLでありません。

+0

'quoteList'は' List 'ではありませんか?もしそうなら、QuoteServiceというメンバーはいないでしょう。あなたは 'quote.QuoteService .....'を意味しましたか? – juharr

+0

はい、これは間違いです。私は今それを修正しています。 – michael

答えて

0

まず、問題について話しましょう。 私がイメージできる唯一の理由は、DbContext befour です.ForAllはおそらく別のスレッドから完成しましたか?しかし、私はちょうどその時に推測しています。

コードの最適化について、いくつかお勧めします。 .ToList()を使用しているすべての見積もりを最初に評価すると、データベースに多数のレコードがあるとパフォーマンスに影響を及ぼす可能性があるので、熱心な評価をSQLカーソルと交換することをおすすめします。 これは、internalyが使用するコンストラクト/コードを使用することで実現できます。IEnumerable <>より具体的には.GetEnumerator()です。

EFでのIQueriable <の内部実装は、非常に高速なSQLカーソルを作成します。最も重要な点は、.AsParallel()またはParallel.ForEachで使用できることです。どちらも内部的に列挙子を使用します。

これはあまり説明されていませんが、SQL Serverプロファイルを起動して以下のコードを実行すると、サーバーに対して単一の要求が実行され、コードを実行しているマシンのRAMがスパイクは、一度にすべてを取得しないことを意味します。

あなたがintrestedている場合、私はそれについていくつかのランダムな情報を見つけた

https://sqljudo.wordpress.com/2015/02/24/entity-framework-hidden-cursors/

私はデータベースレコード上で動作コードは1からの単純な投影のために、かなり重かった例では、このアプローチを使用しましたが、 .AsParallel(..)または単純なforeach constuctとカーソルを実行して別のタイプに似たように実行することがあります。

ので

DbContext.Quotes 
.AsParallel() 
.WithDegreeOfParallelism(...) 
.ForAll(...) 

を使用すると、良好なパフォーマンスで実行する必要があります。

2番目のアドバイスは、遅延ロードを使用してEFのナビゲーションプロパティにアクセスすることです。これはあなたが知っているようにN + 1選択問題につながります。 だからではなく、この場合にはEF遅延評価に応じての「熱心にナビゲーションプロパティを取得するために良くなるので、我々はこの

DbContext.Quotes 
    .Include(x => x.QuoteService) 
    .AsParallel() 
    .WithDegreeOfParallelism(...) 
    .ForAll(...) 

この方法としてEFウォンQuoteServiceナビゲーションプロパティにアクセスするときについてのコードを書き換えることができますサーバーへの追加のリクエストを行う必要はありません。これにより、パフォーマンスが大幅に向上し、Null参照の問題が修正されるはずです。

私が使用している.Include(..)メソッドの汎用バージョンSystem.Data.Entity名前空間の一部。

あなたのシナリオで許容される場合は、変更の追跡を無効にすることができます。これにより、さらにパフォーマンスが向上します。最終的なコードは次のようになります

var queryViewItems = DbContext.Quotes 
      .AsNoTracking() 
      .Include(x => x.QuoteService) 
      .AsParallel() 
      .WithDegreeOfParallelism(...) 
      .Select(x => { ... }) 
      .ToList(); 
関連する問題