web-dev-qa-db-ja.com

マルチスレッドサーバーでEntity Frameworkを使用するC#

マルチスレッドサーバーでエンティティフレームワークを操作するためのベストプラクティスは何ですか?エンティティフレームワークObjectContextを使用してすべてのデータベースアクションを管理していますが、このコンテキストはスレッドセーフではないことがわかっているため、現時点では、いくつかのdbアクションを実行するためにそれを使用する必要がある場合、安全にするためにlockステートメントで囲みます。これはどうすればいいですか?

40
Eyal

マルチスレッド環境でのEntity Frameworkに関するいくつかの簡単なアドバイス:

  • locksで一意のコンテキストを使用しないでください(シングルトンパターンなし)
  • 提供 ステートレスサービス (リクエストごとに1コンテキストをインスタンス化して破棄する必要があります
  • 可能な限りコンテキストの寿命を短くする
  • 同時実行制御システム を実装してください。楽観的な同時実行性は、Entity Frameworkを使用して簡単に実装できます( how-to )。これにより、最新ではないエンティティを使用するときに、DBの変更が上書きされないようになります。

私は少し混乱していますが、1つのコンテキストを使用するといくつかのキャッチを行うので良いと思ったので、連続したリクエストで同じエンティティを処理する場合、同じコンテキストを使用して新しいコンテキストを作成する方がはるかに高速です毎回。なぜそれが遅くてもスレッドセーフではないのであれば、なぜこのように使用するのが良いのでしょうか?

あなたはcouldコンテキストを1つだけ使用しますが、何をしているのか本当にわかっていない限り、/ ** /はお勧めできません

このようなアプローチでよく発生する主な問題は2つあります。

  1. コンテキストが破棄されることはなく、操作されたすべてのエンティティがメモリにキャッシュされるため、大量のメモリを使用します(クエリの結果に表示される各エンティティがキャッシュされます)。

  2. 別のプログラム/コンテキストからデータを変更すると、多くの並行性の問題に直面します。たとえば、データベース内の何かを直接変更し、関連付けられたエンティティが一意のコンテキストオブジェクトに既にキャッシュされている場合、コンテキストは、変更で直接行われた変更について知らないデータベース。最新ではないキャッシュされたエンティティを操作し、私を信じてください。それは、見つけるのが難しい問題を引き起こすでしょう。

また、複数のコンテキストを使用するパフォーマンスについて心配しないでください:リクエストごとに新しいコンテキストを作成/破棄するオーバーヘッドは、ユースケースの90%でほとんど重要ではありません。新しいコンテキストを作成しても、必ずしもデータベースへの新しい接続が作成されるとは限らないことに注意してください(通常、データベースは接続プールを使用するため)。

69
ken2k

これはどうすればいいですか?

いいえ。少なくとも、スレッドごとにコンテキストを使用しますが、コンテキストを作業単位として考えることを強くお勧めします。したがって、スレッドごとに作業単位ごとにコンテキストを使用します。

アプリケーションの「作業単位」を定義するのはあなた次第です。ただし、lockを使用して、複数のスレッドでコンテキストを使用しないでください。スケーリングしません。

12
jason

ObjectContextを非常に高価なエンティティのように扱っているため、一度インスタンス化してから「ファサード」として扱っています。これを行う必要はありません。 ObjectContext抽象化を使用するために「オブジェクトのチェーン」を完全にセットアップするために、他の理由がない限り、接続がボンネットの下にプールされ、ごくわずか(マイクロ秒?-おそらく少ない?).

ObjectContextは、SqlConnectionの直接使用などによく似ており、「可能な限り遅くインスタンス化し、できるだけ早くダンプする」方法で使用されるように設計されています。

EFは、コミットする前に最新のオブジェクトがあるかどうかをテストできるという点で、ある程度の安全性を提供します(オプティミスティック同時実行性)。これは本質的に「スレッドセーフ」を意味するものではありませんが、ルールを尊重すれば同じことを実現します。

5

通常、ObjectContextはアプリケーション全体でグローバルに使用しないでください。新しいObjectContextを頻繁に作成し、古いものを破棄する必要があります。確かにスレッドセーフでもありません。 (アプリケーションの存続期間に応じて)同じObjectContextを引き続き使用する場合、変更するエンティティへの参照はオブジェクトコンテキストによって保持されるため、大量のデータを変更するとメモリ不足例外が発生しやすくなります。

2
Devin

アトミック操作ごとに新しいコンテキストを作成し、コンテキストを破棄します。本や記事から知る限り、Contextの寿命をできるだけ短くすることを好みます。 (ただし、アプローチ方法とアプリケーションの種類、winformまたはwebに依存します)

詳細については、すばらしい記事をご覧ください。 http://www.west-wind.com/weblog/posts/2008/Feb/05/Linq-to-SQL-DataContext-Lifetime-Management

良い本: http://books.google.co.th/books?id=Io7hHlVN3qQC&pg=PA580&lpg=PA580&dq=DbContext+lifetime+for+desktop+application&source=bl&ots=ogCOomQwEE&sig=At3G1Y6AbbJH7OHxmm ZvJo0Yt8&hl = th&ei = rSlzTrjAIovOrQeD2LCuCg&sa = X&oi = book_result&ct = result&resnum = 2&ved = 0CCgQ6AEwAQ#v = onepage&q&f = false

WinForm Binding ScenarioのDatacontext Lifetimeでの既存のディスカッション

1
Pongsathon.keng

マルチスレッド環境でエンティティフレームワークを使用します。マルチスレッド環境では、任意のスレッド、UI、およびバックグラウンド(STAとMTAの両方)が同じデータベースを同時に更新できます。新しいバックグラウンドスレッドでの使用開始時にエンティティ接続をゼロから再作成することで、この問題を解決しました。エンティティ接続インスタンスConnectionStringを調べると、一般的な接続インスタンスをリンクするために使用されると思われるリーダーguidが表示されます。エンティティ接続をゼロから再作成することにより、各スレッドのGUID値が異なり、競合は発生していないようです。アセンブリは、モデルが存在するのと同じアセンブリである必要があることに注意してください。

public static EntityConnection GetEntityConnection(
// Build the connection string.

  var sqlBuilder = new SqlConnectionStringBuilder();
  sqlBuilder.DataSource = serverName;
  sqlBuilder.InitialCatalog = databaseName;
  sqlBuilder.MultipleActiveResultSets = true;
  ...
  var providerString = sqlBuilder.ToString();
  var sqlConnection = new SqlConnection(providerString);

// Build the emtity connection.

  Assembly metadataAssembly = Assembly.GetExecutingAssembly();
  Assembly[] metadataAssemblies = { metadataAssembly };
  var metadataBase = @"res://*/{0}.csdl|res://*/{0}.ssdl|res://*/{0}.msl";
  var dbModelMetadata = String.Format(metadataBase, objectContextTypeModelName);
  // eg: "res://*/Models.MyDatabaseModel.csdl|res://*/Models.MyDatabaseModel.ssdl|res://*/Models.MyDatabaseModel.msl"
  var modelMetadataPaths = modelMetadata.Split('|');
  var metadataWorkspace = new MetadataWorkspace(modelMetadataPaths, metadataAssemblies);
  var entityDbConnection = new EntityConnection(metadataWorkspace, sqlConnection);
  return entityDbConnection;
0
David Coleman