2011-10-19 8 views
0

特定のソーステーブル内の各レコードについて、異なるテーブルの変更を行う必要があり、これらの各sourceTableレコードを個別に処理する必要があるビジネスケースがあります。Entity Frameworkアップデートを使用したTPL

MyEntityFrameworkContext ctx; 
foreach (sourceRecord sr in ctx.sourceTable) 
{ 
    try 
    { 
     using (MyEntityFrameworkContext tctx = new MyEntityFramworkContext) 
     { 
      string result1 = MakeUpdatesToSomeOtherTable1(tctx); 
      sr.Result1 = result1; 
      string result2 = MakeUpdatesToSomeOtherTable2(tctx); 
      sr.Result2 = result2; 
      // will be more tables here. 
      using (TransactionScope ts = new TransactionScope) 
      { 
       tctx.SaveChanges; // to save changes made to OtherTable1 and OtherTable2 
       tctx.ExecuteStoreCommand("SQL that makes a few other changes related to sourceRecord to tables that are NOT in the EF context"); 
       ts.Complete(); 
      } 
     } 
    } 
    catch (Exception ex) 
    { 
    sr.ExceptionResult = ex.Message(); 
    } 
}); 
ctx.SaveChanges(); // to save all changes made to sourceTable. 

ループ内のTCtxとのTransactionScopeの理由は、私はへの呼び出しで1つのトランザクションで保存するOtherTables1 & 2への変更を必要とするということである。

は、だから私は、次のpseodocodeを持っています処理される各sourceRecordのためのtctx.ExecuteStoreCommand()。

また、私は、SourceTableに書き込まれた結果を、TransactionScopeで更新されたテーブルの変更とは別に保存する必要があることにも注意してください。したがって、同じTransactionScopeにsourceTableの更新を含めることはできません。なぜなら、txnがロールバックされると、例外の記録がないからです。このようにして、プロセス全体の最後に、どのソースレコードが失敗したのか、どのレコードが成功したのかを知ることができます。

上記の擬似コードは完全に機能します。

ただし、ここでは並列処理を利用して、foreachをParallel.ForEach()に変換しました。しかし、ts.Complete()、NullReferenceExceptionを呼び出した後、TransactionAbortedExceptionのような予期しないエラーが発生しました.ctx.SaveChanges()を呼び出すとき、またはsourceRecordのResultプロパティの1つを設定すると、時々InvalidOperationExceptionが発生します:EntityMemberChangedまたはEntityComplexMemberChangedは同じプロパティ名を持つ同じ変更トラッカーで最初にEntityMemberChangingまたはEntityComplexMemberChangingを呼び出します)。

したがって、並列性はQUERIESには適していますが、EntityFramworkのデータ更新には適していないと思いますか?私は何が欠けているのか、あるいは並列性について理解していないのですか?なぜ私の上記のアプローチが並列化を使用するように変換されたときに壊れているのか分かりません。アドバイスをいただければ幸いです。

答えて

1

EFオブジェクトのコンテキストはスレッドセーフではないため、複数のスレッドが同じコンテキストで鳴っているときに致命的なエラーが発生する可能性が高くなります。

foreachループの外側に少なくとも1つのコンテキストオブジェクトがあり、スレッド間で共有されているようです。

説明に基づいて、複数のスレッドからのsourceRecordエンティティのプロパティを更新すると、コンテキスト内の内部状態が壊れていると推測できます。おそらく変更追跡のために保持しているデータの集合でしょう。

+0

WCFサービスを作成して、これをtry {}ブロック内で呼び出すことで解決しました。上記のコードに従って新しいEFコンテキスト。それで私が対処しなければならなかった問題はデッドロックと関係していましたが、それはサービス方法の中で何度も各投稿を試すことで簡単に解決されました。 –

0

並列処理は、CPUにバインドされている操作にのみ効果があります.IO操作(データベースの更新など)が行われているように見えるので、作成することで利益を得ることはできませんそれは平行です。しかし、このコードでCPUにバインドされている操作がある場合は、各更新に対して並列にすることができますが、更新は順次実行されます。

+0

mmm私はあなたの開始声明に同意しません。 I/Oを呼び出すシナリオ(Webからダウンロードするか、この場合、EFコンテキストでSaveChangesを呼び出してデータベースに書き込むか)を問わず、特定の利点を提供するために、並列処理を理解しています。私のシナリオでは、一連のテーブル更新の反復が多数必要であり、各反復は他のものと分離されています。確かにこれは並列処理の理想的な候補ですか? –

+0

非同期プログラミングと並列プログラミングには違いがあり、非同期プログラミングはIOに適しています。複数のスレッドがIOリソースにアクセスすると、ロックが含まれるため、並列処理の利点は得られません。 – Ankur

関連する問題