新しいDbContext()をいつ作成する必要がありますか


83

私は現在DbContextこれに似たものを使用しています:

namespace Models
{
    public class ContextDB: DbContext
    {

        public DbSet<User> Users { get; set; }
        public DbSet<UserRole> UserRoles { get; set; }

        public ContextDB()
        {

        }
    }
}

次に、データベースへのアクセスが必要なすべてのコントローラーの上部にある次の行を使用しています。また、ユーザーに関連するすべてのメソッド(アクティブなユーザーの取得、ユーザーの役割の確認など)を含むUserRepositoryクラスでも使用しています。

ContextDB _db = new ContextDB();

これについて考えると、1人の訪問者が複数のDbContextをアクティブにできる場合があります。彼がUserRepositoryを使用するコントローラーにアクセスしている場合、これは最善のアイデアではない可能性があります。いくつか質問があります。

  1. いつ新しいDbContextを作成する必要がありますか/渡すグローバルコンテキストを1つ持つ必要がありますか?
  2. すべての場所で再利用する1つのグローバルコンテキストを持つことはできますか?
  3. これによりパフォーマンスが低下しますか?
  4. 他のみんなはどうやってこれをやっていますか?

重複としてフラグを立てる必要がありました-かなり良い議論についてはstackoverflow.com/questions/12871666/linq-and-datacontextを参照してください
SAJ14SAJ 2012年

2
この場合、依存性注入(Ninjectなど)を使用するので、DbContextリクエストごとに1つ作成されます。サービスレイヤーも作成します。このSOの質問と回答を
Zbigniew 2012年

回答:


82

DataBase派生コントローラーがアクセスできるプロパティを公開するベースコントローラーを使用します。

public abstract class BaseController : Controller
{
    public BaseController()
    {
        Database = new DatabaseContext();
    }

    protected DatabaseContext Database { get; set; }

    protected override void Dispose(bool disposing)
    {
        Database.Dispose();
        base.Dispose(disposing);
    }
}

私のアプリケーションのすべてのコントローラーは、次のように派生しBaseControllerて使用されます。

public class UserController : BaseController
{
    [HttpGet]
    public ActionResult Index()
    {
        return View(Database.Users.OrderBy(p => p.Name).ToList());
    }
}

今あなたの質問に答えるために:

いつ新しいDbContextを作成する必要がありますか/渡すグローバルコンテキストを1つ持つ必要がありますか?

コンテキストはリクエストごとに作成する必要があります。コンテキストを作成し、それを使って必要なことを実行してから、それを取り除きます。私が使用する基本クラスのソリューションでは、コンテキストの使用について心配するだけで済みます。

グローバルコンテキストを試してはいけません(これはWebアプリケーションの動作方法ではありません)。

すべての場所で再利用する1つのグローバルコンテキストを持つことはできますか?

いいえ、コンテキストを維持すると、すべての更新、追加、削除などが追跡され、アプリケーションの速度が低下し、アプリケーションに非常に微妙なバグが発生する可能性があります。

おそらく、リポジトリまたはコンテキストのいずれかをコントローラーに公開することを選択する必要がありますが、両方を公開することはできません。2つのコンテキストが同じメソッドからアクセスされていると、両方がアプリケーションの現在の状態について異なる考えを持っている場合、バグが発生します。

個人的には、私DbContextが見たほとんどのリポジトリの例は、DbContextとにかく薄いラッパーとして終わるので、直接公開することを好みます。

これによりパフォーマンスが低下しますか?

初めてaDbContextを作成するのはかなり費用がかかりますが、一度作成すると多くの情報がキャッシュされるため、その後のインスタンス化がはるかに高速になります。データベースにアクセスする必要があるたびにコンテキストをインスタンス化するよりも、コンテキストを維持することでパフォーマンスの問題が発生する可能性が高くなります。

他のみんなはどうやってこれをやっていますか?

場合によります。

一部の人々は、依存性注入フレームワークを使用して、コンテキストの具体的なインスタンスが作成されたときにコントローラーに渡すことを好みます。どちらのオプションでも問題ありません。Mineは、使用されている特定のデータベースが変更されないことがわかっている小規模なアプリケーションに適しています。

これを知ることができないと主張する人もいるかもしれません。そのため、アプリケーションの変更に対する耐性が高まるため、依存性注入方式の方が優れています。これについての私の意見は、おそらく変更されないだろう(SQLサーバーとEntity Frameworkはほとんど曖昧ではない)、そして私の時間は私のアプリケーションに固有のコードを書くのに最もよく費やされるということです。


4
自分の好みのいくつかが他の人の投稿に反映されているのを見るのはいいことですが、これに追加するものは何もありません。何も追加せずに、DbContextにさらに別のレイヤーが追加されるリポジトリの例を実際に理解したことはありません。保護されたDbContextを公開する基本クラスを作成するのが好きです。
dark_perfect 2013年

4
この答えは素晴らしいですが、使用するたびにコンテキストを自動的に破棄するEF6のリリースにより、現在は古くなっていることを付け加えたいと思いました。そのため、セッションごとの(グローバル)コンテキストの作成が問題ないシナリオがあります。ストアドプロシージャへの接続にEF6を使用していて、データロックが問題にならない場合は、ベースコントローラーでコンテキストを一度作成し、データベースアクセスが必要なすべてのコントローラーをベースから継承することが理想的です。最も正しい答えは、テクノロジーに遅れずについていき、アーキテクチャに正しいパターンを適用する必要があるということです。
Atters 2015年

DbContextを使用するコントローラーアクションからいくつかのモデルメソッドを呼び出したい場合はどうなりますか?モデルメソッドに渡すにはどうすればよいですか?
RMazitov 2015年

次に、それらが属していないコントローラーでデータベースクエリとビジネスロジックを実行します。コントローラから呼び出されるサービスを作成する必要があります。つまり、UserControllerがUserServiceのメソッドを呼び出します。
フレッド

@ Fred、はい、そして関数への単純なパラメーターまたはいいえによって、DbContextをUserServiceに渡すにはどうすればよいですか?
RMazitov 2015

10

私は自分の経験から答えようとします。

1.いつ新しいDbContextを作成する必要がありますか/渡すグローバルコンテキストを1つ持つ必要がありますか?

コンテキストは依存性注入によって注入されるべきであり、自分でインスタンス化されるべきではありません。ベストプラクティスは、依存性注入によってスコープ付きサービスとして作成することです。(質問4に対する私の回答を参照してください)

また、コントローラー>ビジネスロジック>リポジトリなどの適切な階層化されたアプリケーション構造の使用を検討してください。この場合、コントローラーがdb-contextを受信するのではなく、リポジトリを受信します。コントローラでdb-contextを注入/インスタンス化すると、アプリケーションアーキテクチャが多くの責任を1つの場所に混在させていることがわかります。これは、どのような状況でも、お勧めできません。

2.すべての場所で再利用する1つのグローバルコンテキストを持つことはできますか?

はい、できますが、質問は「必要があります...」->いいえです。コンテキストは、リクエストごとに使用してリポジトリを変更し、その後再びリポジトリを変更することを目的としています。

3.これにより、パフォーマンスが低下しますか?

はい、そうです。DBContextは単にグローバルになるように作成されていないからです。入力またはクエリされたすべてのデータは、破棄されるまで保存されます。つまり、グローバルコンテキストはどんどん大きくなり、メモリ不足の例外が発生するか、すべてがクロールまで遅くなるために年齢がなくなるまで、グローバルコンテキストの操作はどんどん遅くなります。

また、複数のスレッドが同時に同じコンテキストにアクセスすると、例外や多くのエラーが発生します。

4.他のみんなはどうやってこれをやっていますか?

依存性注入によって注入されたDBContext-ファクトリによる注入。スコープ:

services.AddDbContext<UserDbContext>(o => o.UseSqlServer(this.settings.DatabaseOptions.UserDBConnectionString));

私の答えが助けになることを願っています。


ConfigureServicesメソッド自体のStartup.csでDBContextを使用したい場合はどうなりますか?DBにアクセスする必要があるOICDミドルウェアがありますが、DBContextにアクセスできないか、方法がわかりませんか?
bbrinck

configureServicesメソッドでは、DBContextを構成したため、DBContextが使用できない可能性があります。実行時に実際にDBContextを取得するServiceProviderは、最初にConfigure()( "ConfigureServices"ではありません!)メソッドで使用可能になります。そこで、「app.ApplicationServices.GetRequiredService <MyDbContext>();」と入力して、ApplicationBuilderを使用してコンテキストをリクエストできます。(MyDbContextを独自のコンテキストのクラス名に置き換えます)。
Ravior

コントローラーがリポジトリーを注入した場合、コントローラー(コントローラー)はどのように変更を保存しますか?データベースに何かを挿入するためにPOSTリクエストを送信しているとしましょう。コントローラーはリクエストを処理し、リポジトリを使用して新しく作成されたオブジェクトを追加します。誰が変更を永続化しますか?
MMalke

@MMalkeリポジトリはそれを行います。通常、関数「SaveData(Data myData)」があり、リポジトリはData-ClassのEntity-Framework兄弟のインスタンスを作成し、それをdbcontextの対応するdbsetに追加してから、SaveChanges()を呼び出します。コントローラが行う唯一のことは、Repository.SaveData(myData)関数を呼び出すことです。
Ravior

1

現在、私はこのアプローチを試しています。これは、コンテキストを使用しないアクションを呼び出すときにコンテキストをインスタンス化することを回避します。

public abstract class BaseController : Controller
{
    public BaseController() { }

    private DatabaseContext _database;
    protected DatabaseContext Database
    {
        get
        {
            if (_database == null)
                _database = new DatabaseContext();
            return _database;
        }
    }

    protected override void Dispose(bool disposing)
    {
        if (_database != null)
            _database.Dispose();
        base.Dispose(disposing);
    }
}

2
あなたのアプローチに問題はありませんが、Entity Frameworkコンテキストはすべてを怠惰な方法で実行し、実際にデータベースにアクセスするまで実際の作業は実行されないと思います。したがって、EFコンテキストを作成するオーバーヘッドは非常に小さいはずです。
Martin Liversage 2015

私はいくつかの調査をしました、そしてそれは正しいようです。GC.GetTotalMemory()返されたもの(完全ではありませんが、私が見つけたものです)を比較することによっていくつかの簡単なテストを行いましたが、その差は8Kbを超えることはありませんでした。
アンドリュー

0

これは明らかに古い質問ですが、DIを使用している場合は、このようなことを実行して、リクエストの存続期間中、すべてのオブジェクトのスコープを設定できます。

 public class UnitOfWorkAttribute : ActionFilterAttribute
    {
        public override void OnActionExecuting(HttpActionContext actionContext)
        {
            var context = IoC.CurrentNestedContainer.GetInstance<DatabaseContext>();
            context.BeginTransaction();
        }

        public override void OnActionExecuted(HttpActionExecutedContext actionContext)
        {
            var context = IoC.CurrentNestedContainer.GetInstance<DatabaseContext>();
            context.CloseTransaction(actionContext.Exception);
        }
    }

0

各Save()操作の直後にコンテキストを破棄する必要があります。そうしないと、後続の各保存に時間がかかります。複雑なデータベースエンティティをサイクルで作成して保存するプロジェクトがありました。驚いたことに、サイクル内で「using(var ctx = new MyContext()){...}」を移動した後、操作は3倍速くなりました。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.