[解決済み] [Solved] Webリクエストごとに1つのDbContext...なぜ?
質問
Entity Frameworkの設定方法を説明した記事をたくさん読みました。
DbContext
様々なDIフレームワークを使用して、HTTPウェブリクエストごとに1つだけ作成され使用されるようにする。
そもそも、なぜこれが良いのでしょうか?この方法を使うことで、どんなメリットがあるのでしょうか?これが良いアイデアとなる特定の状況があるのでしょうか?をインスタンス化するときにできないことで、この手法でできることはありますか?
DbContext
をリポジトリメソッド呼び出しごとに実行できますか?
解決方法は?
<ブロッククオート
注:この回答は、Entity Frameworkの
DbContext
しかし
のような、あらゆる種類の作業単位の実装に適用できます。
LINQ to SQLの
DataContext
と、NHibernateの
ISession
.
まず、Ian の意見に従います。
DbContext
をアプリケーション全体に使用するのは、バッドアイディアです。これが意味を持つ唯一の状況は、シングルスレッド・アプリケーションと、その単一のアプリケーション・インスタンスによってのみ使用されるデータベースがある場合です。その場合
DbContext
はスレッドセーフではなく、また
DbContext
を必要な人に入れることができます。(なぜ単一の
DbContext
-を、あるいはスレッドごとのコンテキストを使用するのはよくありません。
この回答
).
まず最初に言っておくと
DbContext
しかし、通常、このような作業単位のインスタンスは、特定のスコープ内で1つだけ持つようにしたいものです。ウェブアプリケーションでは、ウェブリクエストの境界でそのようなスコープを定義することが実用的です。したがって、Per Web Requestライフスタイルとなります。これにより、一連のオブジェクトを同じコンテキストで動作させることができます。言い換えれば、それらは同じビジネストランザクションの中で操作されます。
もし、一連の操作を同じコンテキスト内で動作させるという目的がないのであれば、その場合はtransient lifestyleで問題ありませんが、いくつか注意しなければならない点があります。
-
すべてのオブジェクトはそれ自身のインスタンスを取得するため、システムの状態を変更するすべてのクラスは、そのインスタンスに対して
DbContext
(そうでなければ、変更が失われてしまう)。これはコードを複雑にし、コードに2つ目の責任(コンテキストを制御する責任)を追加することになります。 単一責任の原則 . -
エンティティ[ロードとセーブは
_context.SaveChanges()
なぜなら、他のクラスのコンテキストインスタンスで使用することができないからです。これはコードを非常に複雑にします。なぜなら、それらのエンティティが必要になったとき、idによってそれらを再びロードする必要があり、パフォーマンスの問題を引き起こす可能性もあるからです。 -
以来
DbContext
を実装しています。DbContext
しかし、作成されたインスタンスをすべて破棄したい場合もあることでしょう。これを行う場合、基本的に2つの選択肢があります。を呼び出した直後に、同じメソッドでそれらのインスタンスを破棄する必要があります。IDisposable
しかし、この場合、ビジネスロジックは外部から渡されたオブジェクトの所有権を取得することになります。2つ目の選択肢は、Httpリクエストの境界で生成されたすべてのインスタンスを破棄することですが、この場合でも、これらのインスタンスを破棄する必要があるときにコンテナに知らせるために、何らかのスコープが必要です。
もう一つの選択肢は
ない
を注入します。
context.SaveChanges()
を全く使用しません。その代わりに
DbContext
のインスタンスを作成することができます(以前はこの方法を使用していました)。こうすることで、ビジネスロジックが明示的にコンテキストを制御することができます。以下のような感じでしょうか。
DbContextFactory
の寿命を管理することができます。
public void SomeOperation()
{
using (var context = this.contextFactory.CreateNew())
{
var entities = this.otherDependency.Operate(
context, "some value");
context.Entities.InsertOnSubmit(entities);
context.SaveChanges();
}
}
を明示的に設定することができ、その設定も簡単です。また、特定のスコープで単一のコンテキストを使用できるため、単一のビジネストランザクションでコードを実行したり、エンティティを受け渡しできるなど、明確な利点があります。
DbContext
.
欠点は、その周りに
DbContext
メソッドからメソッドへ(これはメソッド・インジェクションと呼ばれる)。ある意味、この解決策は「スコープ付き」アプローチと同じですが、今はアプリケーションコード自体でスコープを制御している(そして何度も繰り返される可能性がある)ことに注意してください。作業単位の作成と廃棄を担当するのは、アプリケーションです。このため
DbContext
依存関係グラフが構築された後に作成されるため、コンストラクタ注入は除外され、あるクラスから他のクラスにコンテキストを渡す必要がある場合は、メソッド注入に委ねる必要があります。
メソッド・インジェクションはそれほど悪いものではありませんが、ビジネスロジックがより複雑になり、より多くのクラスが関与するようになると、メソッドからメソッド、クラスからクラスへと受け渡す必要があり、コードが非常に複雑になります(私は過去にこれを経験したことがあります)。シンプルなアプリケーションであれば、この方法で問題ないでしょう。
このファクトリー・アプローチは大規模なシステムには不利なので、別のアプローチも有用です。 コンポジションルート は作業単位を管理します。これがご質問のスタイルです。
コンテナやインフラにこれを処理させることで、アプリケーションのコードはUoWインスタンスの作成、(オプションで)コミット、ディスポによって汚染されることがなくなり、ビジネスロジックをシンプルかつクリーンに保つことができます(単一の責任だけです)。このアプローチにはいくつかの難点があります。例えば、インスタンスのCommitとDisposeはどのように行うのでしょうか?
ユニットオブワークの廃棄は、ウェブリクエストの終了時に行うことができます。しかし、多くの人は 誤って をコミットする場でもあると想定している。しかし、アプリケーションのその時点では、作業単位が実際にコミットされるべきかどうかを確実に判断することはできません。例えば、ビジネス層のコードが例外を投げ、コールスタックの上位でキャッチされた場合、あなたは間違いなく しない コミットしたい。
本当の解決策は、ある種のスコープを明示的に管理することですが、今回はComposition Rootの内部でそれを行います。すべてのビジネスロジックを コマンド/ハンドラパターン そのため、各コマンドハンドラにラップできるデコレータを書くことができます。例
DbContext
これにより、このインフラストラクチャー・コードを一度だけ書く必要があることを保証します。しっかりした DI コンテナであれば、このようなデコレータを設定して、すべての
class TransactionalCommandHandlerDecorator<TCommand>
: ICommandHandler<TCommand>
{
readonly DbContext context;
readonly ICommandHandler<TCommand> decorated;
public TransactionCommandHandlerDecorator(
DbContext context,
ICommandHandler<TCommand> decorated)
{
this.context = context;
this.decorated = decorated;
}
public void Handle(TCommand command)
{
this.decorated.Handle(command);
context.SaveChanges();
}
}
の実装を一貫して行うことができます。
関連
-
[解決済み】2つ(またはそれ以上)のリストを1つに統合する(C# .NETで
-
[解決済み] 関数を終了するには?
-
[解決済み] リクエストの最大長を超えました。
-
[解決済み] なぜList<T>を継承しないのですか?
-
[解決済み] EqualsメソッドがオーバーライドされたときにGetHashCodeをオーバーライドすることが重要な理由は何ですか?
-
[解決済み] C#でHashtableよりDictionaryが好まれる理由とは?
-
[解決済み] HTTP POST Web リクエストの作成方法
-
[解決済み] あるコンストラクタを別のコンストラクタから呼び出す
-
[解決済み] ASP.NET WebサイトとASP.NET Webアプリケーションのどちらを選ぶか?
-
[解決済み] なぜ依存性注入を使用するのですか?
最新
-
nginxです。[emerg] 0.0.0.0:80 への bind() に失敗しました (98: アドレスは既に使用中です)
-
htmlページでギリシャ文字を使うには
-
ピュアhtml+cssでの要素読み込み効果
-
純粋なhtml + cssで五輪を実現するサンプルコード
-
ナビゲーションバー・ドロップダウンメニューのHTML+CSSサンプルコード
-
タイピング効果を実現するピュアhtml+css
-
htmlの選択ボックスのプレースホルダー作成に関する質問
-
html css3 伸縮しない 画像表示効果
-
トップナビゲーションバーメニュー作成用HTML+CSS
-
html+css 実装 サイバーパンク風ボタン
おすすめ
-
[解決済み】"The ConnectionString property has not been initialized "を修正する方法
-
[解決済み】リソースの読み込みに失敗した:ステータス500(内部サーバーエラー)のサーバーの応答)
-
[解決済み】WPFでXamlファイルにコメントを追加する方法は?
-
[解決済み】Visual Studio: 操作を完了できませんでした。パラメータが正しくありません
-
[解決済み】5.7.57 SMTP - MAIL FROMエラー時に匿名メールを送信するためにクライアントが認証されない
-
[解決済み】ランダムなブーリアンを生成する最速の方法
-
[解決済み] 2つのリストを結合する
-
[解決済み】aspNetCore 2.2.0 - AspNetCoreModuleV2 エラー
-
[解決済み】エラー「必要なフォーマルパラメータに対応する引数が与えられていない」を解決する?
-
[解決済み】WebResource.axdとは何ですか?