web-dev-qa-db-ja.com

ASP.NET MVCの要求ごとに1つのDbContext(なしIOCコンテナー)

これが既に回答されている場合はおologiesびしますが、IOCコンテナーを使用していない場合、リクエストごとに1つのEntity Framework DbContextをどのように保証しますか? IOCコンテナソリューション。)

ほとんどのソリューションはHttpContext.Current.Items辞書ですが、リクエストの終了時にDbContextの破棄をどのように保証しますか? (または、EF DbContextを使用して廃棄することは絶対に必要ではありませんか?)

編集

現在、コントローラーでDbContextのインスタンス化と破棄を行っていますが、ActionFiltersとMembershipProviderでDbContextのインスタンス化をいくつか個別に行っています(また、いくつかのバリデーターにも気付きました)。そのため、オーバーヘッドを削減するために、DbContextのインスタンス化とストレージを一元化することをお勧めします。

49
devuxer

BeginRequest/EndRequestメソッドを使用します。これにより、リクエストが終了したときにコンテキストが適切に破棄されます。

protected virtual void Application_BeginRequest()
{
    HttpContext.Current.Items["_EntityContext"] = new EntityContext();
}

protected virtual void Application_EndRequest()
{
    var entityContext = HttpContext.Current.Items["_EntityContext"] as EntityContext;
    if (entityContext != null)
        entityContext.Dispose();
}

そして、EntityContextクラスで...

public class EntityContext
{
    public static EntityContext Current
    {
        get { return HttpContext.Current.Items["_EntityContext"] as EntityContext; }
    }
}
60
Chad Moran

これは最近の質問ではないことを知っていますが、誰かが役に立つと思うので、とにかく答えを投稿します。

おそらく他の多くの人と同じように、私は受け入れられた答えに記載されている手順に従った。はい、動作します。 [〜#〜] however [〜#〜]、キャッチが1つあります。

メソッドBeginRequest()およびEndRequest()リクエストが行われるたびに起動する。ただし、aspxページだけでなく、すべての静的コンテンツに対しても!つまり、上記のコードを使用してページに30個の画像がある場合、dbcontextを30回再インスタンス化することになります。

これに対する解決策は、次のようなコンテキストを取得するためにラッピングクラスを使用することです。

internal static class ContextPerRequest
{
      internal static DB1Entities Current
      {
          get
          {
              if (!HttpContext.Current.Items.Contains("myContext"))
              {
                  HttpContext.Current.Items.Add("myContext", new DB1Entities());
              }
              return HttpContext.Current.Items["myContext"] as DB1Entities;
          }
      }
 }

そして、処分のために

protected void Application_EndRequest(object sender, EventArgs e)
{
   var entityContext = HttpContext.Current.Items["myContext"] as DB1Entities;
   if (entityContext != null) 
      entityContext.Dispose();
}

この変更により、リクエストごとに必要な場合にのみコンテキストをインスタンス化して破棄することが保証されます。選択された回答は、毎回コンテキストをインスタンス化します。

注: DB1EntitiesはDbContextから派生します(VSによって生成されます)。あなたはおそらくあなたのコンテキスト名でそれを変更したいと思うでしょう;)

注2:この例では、dbcontextを1つだけ使用しています。複数で作業する必要がある場合は、必要に応じてこのコードを変更する必要があります。確かに最終製品ではないので、これを世界の問題に対する究極の解決策として受け取らないでください。これは、非常に簡単な方法でどのように達成できるかを示すためのものです。

注3:さまざまな状況でも同じアプローチを使用できます。たとえば、SqlConnectionまたはその他のインスタンスを共有する場合などです。このソリューションはDbContextオブジェクトに限定されません。エンティティフレームワークにも。

73
walther

1つの方法は、Application_BeginRequestイベント、DbContextを現在のHttpContextおよびApplication_EndRequest HttpContextからフェッチして破棄します。間にあるもの(ほとんどすべて:-)は、現在のHttpContextからDbContextを取得して使用できます。そして、はい、あなたはそれを処分する必要があります。ところで、DIフレームワークを使用しない理由は他にもありますが、他にも便利なものがありますか?

10
Darin Dimitrov

チャド・モランの回答の小さな追加。ワルターノートに触発されています。静的コンテンツのコンテキストの初期化を回避するには、現在のルートハンドラーを確認する必要があります(この例はMVCのみ)。

protected virtual void Application_BeginRequest()
{
  var routeData = RouteTable.Routes.GetRouteData(new HttpContextWrapper(this.Context));
  if (routeData != null && routeData.RouteHandler is MvcRouteHandler)
  {
    HttpContext.Current.Items["_EntityContext"] = new EntityContext();
  }
}
7
DrunkCoder

コントローラーにIDisposableを実装し、破棄メソッドにコンテキストを配置し、コントローラーコンストラクターに新しいコンテキストをインスタンス化する場合、コントローラーはリクエストごとにインスタンス化されるため安全です。しかし、どうしてそんなことをしたいのかわかりません。 ... DIを使用するか、コンテキストの静的インスタンスを1つ持つコンテキストファクトリを作成する必要があります。 1つのインスタンスを使用しない場合(リクエストごとに1つ作成します)、ある時点で問題が発生します。破棄されていないコンテキストの問題は、EFがコンテキスト内のデータをキャッシュし、コンテキストの他のインスタンスが既に別のコンテキストにキャッシュされているDBの何かを変更する場合-一貫性のない状態になります。 DIが非常に普及する前は、アプリケーションのどこかにコンテキストの静的インスタンスが1つあり、各リクエストが独自のコンテキストを作成するよりもはるかに高速かつ安全ですが、コンテキストを確認する状態チェックコードを実装する必要がありますdbへの接続は問題ありません...この問題に対する解決策は他にもたくさんありますが、DIフレームワークを使用することをお勧めします。 NinjectとMVCTurbineを組み合わせて使用​​することをお勧めします。セットアップは簡単で、NuGetを使用して追加できます。

1
Goran Obradovic

ここの滑りやすい斜面は一貫性のない状態になっています。アプリに複数のユーザーが存在し、それらが同時にデータを変更する可能性がある場合、単一のコンテキストを保持すると、データの整合性に関する問題が発生する可能性があります。

0
mymex1