2012-02-01 8 views
34

マルチスレッドエンティティフレームワークドリブンアプリケーションの設計にはいくつかの問題があり、いくつかのガイダンスが必要です。異なるスレッド上にエンティティを作成し、エンティティはコレクションに追加され、コレクションはさまざまなwpfコントロールにバインドされます。エンティティフレームワークとマルチスレッド

解決策1は単一のコンテキストを持ち、2つのスレッドが同時にアクセスしないように慎重にロックを使用します。これは実装が比較的簡単ですが、アプリケーションの継続中にコンテキストが生きている必要があります。単一のコンテキストインスタンスをこのように開いておくことは悪い考えですか?

解決策2は、必要に応じてコンテキストオブジェクトを作成し、オブジェクトを直ちに切り離してから、独自のコレクションに保持してから再インストールして更新することです。これは、オブジェクトがデタッチされたときにナビゲーションプロパティオブジェクトへの参照を失うので、重大な問題があります。また、2つのスレッドが1つのオブジェクトにアクセスしようとする可能性があり、両方がコンテキストにattach()しようとするという問題もあります。また、エンティティのナビゲーションプロパティにアクセスするたびに新しいコンテキストを提供する必要があります。

Q:2つのソリューションのいずれかが有効ですか?これに対処する方法を教えてください。

+2

あなたはより良いアイデアを持っていますか? – Cocowalla

+0

@Cocowallaは、OPが取り組んでいるより大きなシナリオを知らず、私はしません。彼の解決策の両方が痛ましい実装につながるので、私は彼に警告しています。おそらく、彼はまったく別の道を踏み、EFをシングルスレッドの方法で使用することができます。 – usr

+0

もう1つの注意点:エンティティがデタッチされているときにエンティティに変更を加えることはできません。コンテキストが現在その変更を追跡していないためです。 SaveChanges()が後で呼び出されると、変更は保持されません。 – JoeCool

答えて

23

まず、this article on MSDNと読んでいるとします。

ソリューション#1は、ある時点でコンテキストと対話しているスレッドが1つだけであることを保証しているので、ほぼ確実にスレッドの観点から安全です。コンテキストを維持することに本質的に間違ったものは何もありません。データベース接続をバッ​​クグラウンドで開いたままにしているわけではないため、メモリオーバーヘッドに過ぎません。もちろん、このスレッドでボトルネックになり、アプリケーション全体が1つのdbスレッドの前提で記述されると、パフォーマンス上の問題が発生する可能性があります。

解決策2は私にはうまくいかないようです。エンティティの再接続(または切り離し)を忘れているアプリケーション全体に微妙なバグが発生します。

解決策の1つは、アプリケーションのUIレイヤーでエンティティオブジェクトを使用しないことです。おそらくこれをお勧めします。エンティティオブジェクトの構造/レイアウトは、ユーザーインターフェイス上に物事を表示する方法には最適ではありません(これがMVCパターンファミリの理由です)。 DALには、ビジネスロジック固有のメソッド(たとえば、UpdateCustomer)が必要です。新しいコンテキストを作成するか、格納されたコンテキストを使用するかは、内部的に決定する必要があります。単一のストアド・コンテキスト・アプローチから始めることができます。次に、ボトルネックの問題に遭遇した場合、変更が必要な領域が限られています。

多くのコードを書く必要があるということは、EFエンティティを持つことですが、多くのEFエンティティのプロパティが重複し、潜在的に異なるビジネスエンティティを持つことになります。これを軽減するために、AutoMapperのようなフレームワークを使用して、EFエンティティからビジネスエンティティへのプロパティのコピーを単純化し、再び元に戻すことができます。

+0

クリス、答えに感謝、私たちの考えを明確にするのに非常に役立ちました。私たちは皆さんの推薦を得るつもりです。私たちはエンティティオブジェクトを使ってすべてをやろうとしています。 – MartinR

+2

ソリューション1のもう1つの問題はキャッシュです。コンテキストがエンティティをキャッシュして、アプリケーションの実行中のインスタンスの外部でデータが変更されたときにデータが失効するようにします。 – Cocowalla

+0

あなたが提供したリンクがEFの仕組みに多くの光を与えました。どうもありがとう。 – Brandon

0

長寿命のコンテキストを必要としません。理想的には、それらは要求/データ操作の寿命のためにあるべきです。

同様の問題に対処するとき、特定のタイプのPKエンティティをキャッシュしたリポジトリを実装し、データベース内のエンティティを検索し、PK以外のすべてのスカラープロパティを 'コピー'する 'LoadFromDetached'を許可しました新たに添付されたエンティティに渡します。

パフォーマンスは少しヒットしますが、ナビゲーションプロパティがそれらについて「忘れている」ことによってマングンになっていないことを確認するための徹底的な方法を提供します。

+0

私は最近、似たようなタイプの問題にぶつかって、パフォーマンスを大幅に向上させました。間違った場所に入力した、私の答えを見てください – Otake

0

私は最近、似たようなタイプの問題にぶつかって、パフォーマンス基準を満たすのに役立つ以下のことを終わらせました。

あなたは基本的にあなたのリストをチャンクに分割し、マルチスレッドの方法でそれをスペルスレッド内で処理します。新しいスレッドはそれぞれ、エンティティのアタッチが必要な独自のUIを開始します。

データベースのスナップショットを有効にする必要があることに注意してください。そうしないと、デッドロックが発生する可能性があります。これがあなたがやっている作業とそれに関連するビジネスプロセスに問題ないのかどうかを判断する必要があります。私たちの場合、それは製品エンティティの簡単な更新でした。

最高のチャンクサイズを決定し、並列処理を制限して操作を完了するためのリソースが常にあるようにするには、おそらくいくつかのテストを行う必要があります。

private void PersistProductChangesInParallel(List<Product> products, 
     Action<Product, string> productOperationFunc, 
     string updatedBy) 
    { 
     var productsInChunks = products.ChunkBy(20); 

     Parallel.ForEach(
      productsInChunks, 
      new ParallelOptions { MaxDegreeOfParallelism = 20 }, 
      productsChunk => 
       { 
        try 
        { 
         using (var transactionScope = new TransactionScope(
           TransactionScopeOption.Required, 
           new TransactionOptions { IsolationLevel = IsolationLevel.Snapshot })) 
         { 
          var dbContext = dbContextFactory.CreatedbContext(); 
          foreach (var Product in productsChunk) 
          { 
           dbContext.products.Attach(Product); 
           productOperationFunc(Product, updatedBy); 
          } 
          dbContext.SaveChanges(); 
          transactionScope.Complete(); 
         } 
        } 
        catch (Exception e) 
        { 
         Log.Error(e); 
         throw new ApplicationException("Some products might not be updated", e); 
        } 
       }); 
    } 
7

私は、EFとマルチスレッドに関するいくつかのstackoverflowスレッドがあります。それらのすべてに、問題の深さを説明する回答がありますが、実際に問題を解決する方法は示されていません。

EFはスレッドセーフではありません。私たちはこれまでにすべてを知っています。しかし私の経験では、唯一のリスクは文脈の作成/操作です。 実際には、遅延ロードを保つことができる非常に単純な修正があります。

あなたはWPFアプリケーションとMVC Webサイトを持っているとします。 WPFアプリケーションはマルチスレッドを使用します。あなたはマルチスレッドでdbコンテキストを破棄し、そうでないときはそれを保持します。 MVC Webサイトの例では、ビューが提示された後にコンテキストが自動的に破棄されます。お使いの製品のビジネスロジック層は、のようになりますどのように

ProductBLL productBLL = new ProductBLL(); 

:あなたはこれを使用MVCアプリケーション層で

ProductBLL productBLL = new ProductBLL(true); 

:あなたはこれを使用するWPFアプリケーション層で

public class ProductBLL : IProductBLL 
{ 
    private ProductDAO productDAO; //Your DB layer 

    public ProductBLL(): this(false) 
    { 

    } 
    public ProductBLL(bool multiThreaded) 
    { 
     productDAO = new ProductDAO(multiThreaded); 
    } 
    public IEnumerable<Product> GetAll() 
    { 
     return productDAO.GetAll(); 
    } 
    public Product GetById(int id) 
    { 
     return productDAO.GetById(id); 
    } 
    public Product Create(Product entity) 
    { 
     return productDAO.Create(entity); 
    } 
    //etc... 
} 
データベース・ロジック層はのようになりますどのように

は:

public class ProductDAO : IProductDAO 
{ 
    private YOURDBCONTEXT db = new YOURDBCONTEXT(); 
    private bool _MultiThreaded = false; 

    public ProductDAO(bool multiThreaded) 
    { 
     _MultiThreaded = multiThreaded; 
    } 
    public IEnumerable<Product> GetAll() 
    { 
     if (_MultiThreaded) 
     { 
      using (YOURDBCONTEXT db = new YOURDBCONTEXT()) 
      { 
       return db.Product.ToList(); //USE .Include() For extra stuff 
      } 
     } 
     else 
     { 
      return db.Product.ToList(); 
     }     
    } 

    public Product GetById(int id) 
    { 
     if (_MultiThreaded) 
     { 
      using (YOURDBCONTEXT db = new YOURDBCONTEXT()) 
      { 
       return db.Product.SingleOrDefault(x => x.ID == id); //USE .Include() For extra stuff 
      } 
     } 
     else 
     { 
      return db.Product.SingleOrDefault(x => x.ID == id); 
     }   
    } 

    public Product Create(Product entity) 
    { 
     if (_MultiThreaded) 
     { 
      using (YOURDBCONTEXT db = new YOURDBCONTEXT()) 
      { 
       db.Product.Add(entity); 
       db.SaveChanges(); 
       return entity; 
      } 
     } 
     else 
     { 
      db.Product.Add(entity); 
      db.SaveChanges(); 
      return entity; 
     } 
    } 

    //etc... 
} 
0

私は、マルチスレッドでEFを使用しようとすると、エラーの原因となったプロジェクトを持っていました。

私は

using (var context = new entFLP(entity_connection))    
{ 
    context.Product.Add(entity); 
    context.SaveChanges(); 
    return entity; 
} 

を試みたが、それだけで、複数のスレッドのエラーにDataReaderのエラーからエラーの種類を変更しました。

簡単な解決策は、EF機能の輸入

using (var context = new entFLP(entity_connection)) 
{ 
    context.fi_ProductAdd(params etc); 
} 

キーはデータのソースに移動し、データモデルを避けるためであるとストアドプロシージャを使用することでした。

関連する問題