エンティティフレームワークと接続プーリング


268

最近、.NET 4.0アプリケーションでEntity Framework 4.0の使用を開始しましたが、プールに関連するいくつかのことに興味があります。

  1. 私が知っている接続プーリングはADO.NETデータプロバイダーによって管理されています。私の場合は、MS SQLサーバーの場合です。これは、新しいエンティティコンテキスト(ObjectContext)をインスタンス化するときに適用されますnew MyDatabaseModelEntities()か?

  2. a)アプリケーションのグローバルエンティティコンテキストを作成する(つまり、1つの静的インスタンス)、またはb)usingブロックを使用して、特定の操作/メソッドごとにエンティティコンテキストを作成および公開することの長所と短所は何ですか。

  3. 私が知っておくべき他の推奨事項、ベストプラクティス、または特定のシナリオの一般的なアプローチはありますか?

回答:


369
  1. 接続プーリングは、他のADO.NETアプリケーションと同様に処理されます。エンティティ接続では、従来の接続文字列を使用した従来のデータベース接続が引き続き使用されます。使用したくない場合は、接続文字列の接続プールをオフにできると思います。(SQL Server接続プーリング(ADO.NET)の詳細を読む)
  2. グローバルコンテキストを使用しないでください。ObjectContextは、アイデンティティマップや作業単位などのいくつかのパターンを内部的に実装しています。グローバルコンテキストの使用による影響は、アプリケーションタイプごとに異なります。
  3. Webアプリケーションの場合、リクエストごとに単一のコンテキストを使用します。Webサービスの場合、呼び出しごとに単一のコンテキストを使用します。WinFormsまたはWPFアプリケーションでは、フォームごとまたはプレゼンターごとに単一のコンテキストを使用します。このアプローチを使用できない特別な要件がある場合がありますが、ほとんどの状況ではこれで十分です。

WPF / WinFormアプリケーションの単一オブジェクトコンテキストの影響を知りたい場合は、この記事を確認してください。それはNHibernateセッションについてですが、考え方は同じです。

編集:

EFを使用すると、デフォルトで、各エンティティはコンテキストごとに1回だけロードされます。最初のクエリはエンティティインスタンスを作成し、内部に保存します。同じキーを持つエンティティを必要とする後続のクエリは、この格納されたインスタンスを返します。データストアの値が変更された場合でも、最初のクエリからの値を持つエンティティを受け取ります。これはアイデンティティマップパターンと呼ばれます。オブジェクトコンテキストにエンティティを強制的に再読み込みさせることができますが、単一の共有インスタンスが再読み込みされます。

エンティティに加えられた変更はSaveChanges、コンテキストを呼び出すまで保持されません。複数のエンティティを変更して、一度に保存できます。これは、作業単位パターンと呼ばれます。保存する変更された添付エンティティを選択して言うことはできません。

これらの2つのパターンを組み合わせると、興味深い効果が得られます。アプリケーション全体のエンティティのインスタンスは1つだけです。エンティティへの変更は、変更がまだ永続化(コミット)されていなくても、アプリケーション全体に影響します。ほとんどの場合、これはあなたが望むものではありません。WPFアプリケーションに編集フォームがあるとします。エンティティを操作していて、複雑な編集(値の変更、関連エンティティの追加、他の関連エンティティの削除など)をキャンセルすることにしました。ただし、エンティティは共有コンテキストですでに変更されています。あなたは何をしますか?ヒント:のCancelChangesやUndoChangesについては知りませんObjectContext

サーバーのシナリオについて説明する必要はないと思います。複数のHTTP要求またはWebサービス呼び出し間で単一のエンティティを共有するだけでは、アプリケーションは役に立たなくなります。どんなリクエストでもトリガーできるSaveChanges間で単一の作業単位を共有しているため、、別のリクエストからの部分的なデータをして保存ます。これには別の問題もあります。コンテキストと、コンテキスト内のエンティティを使用した操作、またはコンテキストで使用されるデータベース接続はスレッドセーフではありません。

読み取り専用アプリケーションの場合でも、アプリケーションにクエリを実行するたびに新しいデータが必要になる可能性があるため、グローバルコンテキストは適切な選択ではありません。


お返事をありがとうございます。おそらく、単一のグローバルコンテキストを使用することが悪い理由について詳しく説明できますか?それは確かに並列アクセスを難しくしますが、他に何か...?
Noldorin

わかりました。これでかなり明確になりました。ありがとうございます。確認のために、グローバルコンテキストは決して適切ではありませんが、「編集ダイアログ」などの単一のコンテキストが正しい方法かもしれません。WebサービスやASP.NETなどの他の状況では、メソッド内のコンテキストはより意味があります。正しいですか?
Noldorin

私はあなたの忠告を聞き、シンゲルトンを削除しました。今度は別のエラーが発生します。stackoverflow.com
questions

作業単位パターンの実装とDbContextのカプセル化により、ビジネスロジックとデータベース操作が分離されることを理解しています。作業単位パターンを実装し、一部の操作にのみTransactionScopeを使用する方法を理解できません。
Rudolf Dvoracek 2013年

4
@RudolfDvoracek:簡単に。TransactionScopeロジック自体がトランザクションを定義するため、作業ユニットに属していません。ビジネスロジックに属しています。作業単位では、何を一緒に永続化するかを定義するだけですが、トランザクションスコープでは、同じトランザクション内で作業単位の永続性を複数回使用できます。
Ladislav Mrnka 2013年

70

ダニエル・シモンズによると:

メソッドが戻る前に破棄されるように、各サービスメソッドのUsingステートメントで新しいObjectContextインスタンスを作成します。この手順は、サービスのスケーラビリティにとって重要です。これにより、サービスコール間でデータベース接続が開いたままにならず、特定の操作で使用される一時的な状態が、その操作が終了したときにガベージコレクションされます。Entity Frameworkは、アプリドメインで必要なメタデータとその他の情報を自動的にキャッシュし、ADO.NETはデータベース接続をプールするため、毎回コンテキストを再作成するのが迅速な操作です。

これは彼の包括的な記事からここにあります:

http://msdn.microsoft.com/en-us/magazine/ee335715.aspx

このアドバイスはHTTPリクエストにも当てはまるので、ASP.NETにも当てはまると思います。WPFアプリケーションなどのステートフルなファットクライアントアプリケーションが、「共有」コンテキストの唯一のケースである場合があります。


ありがとう、それはそこに非常に有益な引用です。ただし、クライアントのWPFアプリなどでも、共有(グローバル)コンテキストが適切であるかどうかはまだ疑問です。この場合でもメリットはありますか?
Noldorin

WPFアプリのグローバルコンテキストに利点はありませんが、おそらく大きなデメリットはありません。グローバルコンテキストを実装する場合、要求率が高い場合にデータベース接続の手動管理(接続の明示的なクローズ)を行う必要がある場合があります。
Dave Swersky、2010

1
正しい; したがって、本質的に私は複数の一時的なコンテキストを使用することで本当に失敗することは決してありません(接続プーリングが起こっていることを知っているとすれば)?...単一のグローバルコンテキストを使用している場合、理論的には接続がランダムな時点でドロップできませんでしたか?
Noldorin

1
@Nolodrin:接続が「ランダムに」ドロップすることはないと思います...リスクは、接続が長時間開かれたままになり、接続プールが飽和する可能性があることです。
Dave Swersky、2010

1
ObjectContext / DbContext implementはIDisposable、それゆえ、妥当な最短時間オープンする必要があると私は考えています。
nicodemus13

12

EF6(4,5も)ドキュメントに準拠:https ://msdn.microsoft.com/en-us/data/hh949853#9

9.3リクエストごとのコンテキスト

Entity Frameworkのコンテキストは、最適なパフォーマンスエクスペリエンスを提供するために、短期間のインスタンスとして使用されることを意図しています。コンテキストは短命で破棄されることが期待されているため、非常に軽量で、可能な限りメタデータを再利用するように実装されています。Webシナリオでは、これを念頭に置き、1つの要求の持続時間以上のコンテキストがないことが重要です。同様に、非Webシナリオでは、Entity Frameworkのさまざまなレベルのキャッシュについての理解に基づいて、コンテキストを破棄する必要があります。一般的に言えば、アプリケーションの存続期間を通じてコン​​テキストインスタンスを使用することは避け、スレッドごとのコンテキストや静的コンテキストを使用しないでください。


2
私はこの返信がここにあることを知っていますが、これは私に頭痛のトンを救ったと言わざるを得ません。OracleでEFを使用すると、「プールされた接続」エラーが発生し続け、その理由を理解できませんでした。dbContextをクラス変数として設定し、作成時にインスタンス化しました。必要に応じてコンテキストを作成するように変更することで、私の世界のすべての問題を解決しました。ありがとうございました!
Fletchius 2017

1

以下のコードは、オブジェクトを新しいデータベース値で更新するのに役立ちました。Entry(object).Reload()コマンドは、オブジェクトにデータベース値を強制的に呼び戻します

GM_MEMBERS member = DatabaseObjectContext.GM_MEMBERS.FirstOrDefault(p => p.Username == username && p.ApplicationName == this.ApplicationName);
DatabaseObjectContext.Entry(member).Reload();

同様に、コレクション(VBコード)の場合:CType(myContext, IObjectContextAdapter).ObjectContext.Refresh(RefreshMode.StoreWins,myCustomers)
Ivan Ferrer Villa
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.