web-dev-qa-db-ja.com

Entity Frameworkコンテキストオブジェクトの破棄が必要です

WCFサービスメソッドでデータベースとの通信にエンティティフレームワークを使用しています。最近、サービスコードでコードレビューツールを実行しています。いつものように、ツールごとに多くのレビュー提案があり、Entity Frameworkコンテキストオブジェクトを破棄することを提案する多くのレビューコメントがありました。したがって、私の質問は、メソッド内でEntity Frameworkコンテキストオブジェクトを使用していて、メソッドから出た後、GCがコンテキストオブジェクトをクリーンアップしないのですか?コンテキストオブジェクトを明示的に破棄する必要がありますか?

15
Abhinay

単に: DbContextIDisposableを実装しているため、使い終わったらすぐに手動で破棄する必要があります。

GCは最終的にそれを収集するため、それを処分する必要はありませんが、GCは確定的ではありません。 "になります。破棄されるまで、使用されていないリソースが保持されます。たとえば、開いているデータベース接続がある可能性があります。これらのリソースは、GCが実行されるまで、手動で破棄しない限り解放されます。特定の詳細に応じて、ネットワークリソース、ファイルアクセスを不必要にブロックし、必要以上に多くのメモリを確保していることがわかります。

さらに潜在的なヒットもあります。オブジェクトを手動で破棄する場合、GCは通常、そのオブジェクト(存在する場合)でファイナライザーを呼び出す必要はありません。ファイナライザでオブジェクトを自動的に破棄するようにGCを離れると、オブジェクトはファイナライザキューに配置され、オブジェクトは次のGC生成に自動的に昇格します。つまり、ファイナライザを備えたオブジェクトは、GCが実行される前に必要な桁数よりも常に桁違いに長い時間待機します(連続するGC生成の収集頻度が低くなるため)。 DBContextは、基になるデータベース接続がアンマネージコードになるため、このカテゴリに分類される可能性があります。

(便利 参照 。)

27
Dan Puzey

私は最良のアプローチはusingステートメント内でコーディングすることだと思います

using(var cx = new DbContext())
{
  //your stuff here
}

自動的に処分されました

10
Stefan Michev

一般に、何かがIDisposableを実装している場合は、処理が終わったときに明示的に破棄することがGood Idea(TM)です。これは、上記のオブジェクトの実装を所有していない場合に特に当てはまります。この場合は、ブラックボックスとして扱う必要があります。さらに、今すぐに廃棄する必要が必ずしもなかったとしても、将来的には廃棄される可能性があります。

したがって、オブジェクトを明示的に「必要とする」かどうかという問題は関係ありません。 IDisposableを実装することにより、それが処分されることを求めている場合は、処分する必要があります。

3
lc.

DBContextで行うことをお勧めするのは、使い捨てオブジェクトであっても、まったく破棄しないことです(ほとんどの場合、これはルールの例外です)。

問題の例です。最初の例は、呼び出しを使用してusingステートメントで評価し、2番目の例は後で評価します。 (1回目は実行され、2回目はエラーThe operation cannot be completed because the DbContext has been disposed.をスローします)

List<Test> listT;
using (Model1 db = new Model1())
{
    listT = db.Tests.ToList(); //ToList Evaluates
}
foreach (var a in listT)
{
    Console.WriteLine(a.value);
}

IEnumerable<Test> listT1;
using (Model1 db = new Model1())
{
    listT1 = db.Tests;
}
foreach (var a in listT1) //foreach evaluates (but at wrong time)
{
    Console.WriteLine(a.value);
}

同じ問題が

IEnumerable<Test> listT1;
Model1 db = new Model1();
listT1 = db.Tests;
db.Dispose();
foreach (var a in listT1) //foreach evaluates (but at wrong time)
{
    Console.WriteLine(a.value);
}

手動で接続を開かない限り、使用するだけで安全です

IEnumerable<Test> listT1;
Model1 db = new Model1();
listT1 = db.Tests;
foreach (var a in listT1) //foreach evaluates (but at wrong time)
{
    Console.WriteLine(a.value);
}

そして決して処分しません。ほとんどの状況で自動的に処理されるので、そのように設計されています。

これで、接続を強制的に開いた場合、転送が行われたときにコンテキストが自動的に接続を閉じることはありません。これは、いつ、どのような場合にオブジェクトを破棄するか、接続を閉じて、オブジェクトを非公開にしておく必要があるためです。

深夜の読書:

  1. http://blog.jongallant.com/2012/10/do-i-have-to-call-dispose-on-dbcontext.html#.U6WdzrGEeTw
  2. https://msdn.Microsoft.com/en-us/data/jj729737.aspx
2

DbContextを明示的に破棄する必要はありません。

これは、DbContext以前のツールからの古いホールドです。 DbContextはマネージコードであり、データベース接続をそれ自体で楽観的に維持します。なぜそれをハンマーで叩くのですか?急いで?マシンがアイドル状態またはメモリを必要とするときに、ガベージコレクターがクリーンアップするのに最適な時間を決定するだけではどうですか。この投稿も参照してください: https://blog.jongallant.com/2012/10/do-i-have-to-call-dispose-on-dbcontext/

破棄する必要がないことを心配することで、コードが簡素化および最適化されます。通常、データベースの「ヘルパー」クラスからゲッターを使用して、既存のDbContextインスタンスを返すか、新しいインスタンスをインスタンス化する場所を継承します。 。

public class DataTools
{
    private AppContext _context;
    protected AppContext Context => _context ?? (_context = new AppContext());
}

pubic class YourApp : DataTools
{
    public void DoLotsOfThings()
    {
        var = Context.SomeTable.Where(s => s.....);
        var stuff = GetSomeThing();
       foreach(){}
    }

    Public string GetSomething()
    {
        return Context.AnotherTable.First(s => s....).Value;
    }
}
1
Russ Ebbing