是否需要处理实体框架上下文对象

Posted

技术标签:

【中文标题】是否需要处理实体框架上下文对象【英文标题】:Is Disposing of Entity Framework context object required 【发布时间】:2014-03-19 12:11:09 【问题描述】:

我们在 WCF 服务方法中使用实体框架与数据库通信,最近我们在服务代码上运行代码审查工具。像往常一样,我们通过工具收到了许多审查建议,并且许多审查 cmet 建议处置实体框架上下文对象。所以,我的问题是,如果我在方法中使用实体框架上下文对象,并且一旦我退出该方法,GC 就不会清理上下文对象?我们需要显式地处理上下文对象吗?

【问题讨论】:

你现在如何使用它?我通常在 using 块中使用上下文。 目前没有使用block。 保持它不使用,使用将杀死从其中启动的潜在线程,因为上下文可能会在到达其中的线程启动表单的末尾之前被释放。 我认为许多答案都遗漏了一个关键点,那就是 DBContext 经常与 DependencyInjection 一起使用(在 MVC 中)。在这种情况下,DBContext 不是由服务类创建的,而是由框架注入的。在这种情况下,它不应该在服务类中被释放,因为它不是在类中创建的。 【参考方案1】:

简单地说:DbContext 实现了IDisposable,因此您应该在完成后立即手动处理它。

您不需要处理它,因为 GC 最终会收集它,但 GC 不是确定性的:您永远不知道“最终”会在什么时候。在处理它之前,它将持有未使用的资源 - 例如,它可能仍然有一个打开的数据库连接。在 GC 运行之前,这些资源不会被释放,除非您手动处置。根据具体细节,您可能会发现您不必要地阻止了网络资源、文件访问,并且您肯定会保留比您需要的更多的内存。

还有进一步的潜在影响:当您手动处理对象时,GC 通常不需要调用该对象的终结器(如果有的话)。如果您让 GC 使用 Finalizer 自动处理对象,它将将该对象放入 Finalizer Queue - 并自动将该对象提升到下一代 GC。这意味着带有终结器的对象在被 GC 之前总是会比它需要的时间长几个数量级(因为连续 GC 代的收集频率较低)。 DBContext 可能属于此类,因为底层数据库连接将是非托管代码。

(有用reference。)

【讨论】:

是关于 GC 和 IDisposable 目的的问题,还是特定于 DbContext? 这个问题是针对DbContext 的,但答案是DbContext 与其他IDisposable 完全相同的最佳实践与其他IDisposable 的原因完全相同- 因此是最通用的答案。 这取决于 OP 和支持这个答案的人。我在这里没有看到任何特定于 DbContext 的内容。没有说明应该释放什么样的资源。 我不明白你的反对意见。 OP 的具体问题的答案是:是的,GC 将处理 DbContext,但你不知道何时(根据我第二段的开头),是的,你应该明确处理(根据我的第一句话)。其余的答案是背景和细节,以解释为什么这很重要(对于 any IDisposable)。 DbContext 的具体细节与答案并不特别相关,因为它们不会更改答案(无论如何都是黑盒代码的实现细节)。 实际上看起来您不必处理 DBcontext(按设计)blog.jongallant.com/2012/10/…【参考方案2】:

我认为最好的方法是在 using 语句中对其进行编码

using(var cx = new DbContext())

  //your stuff here

所以它得到了自动处理

【讨论】:

如果我们使用这种方法,我们如何模拟 DBContext 来编写单元测试? 查找存储库和工作单元模式asp.net/mvc/overview/older-versions/… 如果我想在类范围内使用上下文怎么办? (在构造函数中创建它 - 如何以及在哪里关闭它?)【参考方案3】:

一般来说,如果某些东西实现了IDisposable,那么当你完成时明确地处理它是一个好主意(TM)。如果您不拥有所述对象的实现,则尤其如此;在这种情况下,您应该将其视为黑匣子。此外,即使现在不一定“需要”处理它,也可能是在将来。

因此,恕我直言,您是否“需要”明确处置该对象的问题是无关紧要的。如果它要求被处置 - 通过实施IDisposable - 它应该被处置。

【讨论】:

【参考方案4】:

建议对 DBContext 做的事情是根本不丢弃它(在大多数情况下这是规则的例外),即使它是一次性对象。

这个问题的一个例子,第一个例子是在 using 语句中调用并评估它,而第二个例子在之后评估它。 (第一次运行,第二次抛出错误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);

并且从不丢弃。因为它会在大多数情况下自行处理,这就是它的设计目的。

现在如果你强行打开一个连接,那么在传输完成时上下文不会自动关闭它,因为它不知道什么时候你必须/应该处置对象或关闭连接并保持对象不公开.

午夜阅读:

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

【讨论】:

【参考方案5】:

无需显式处置 DbContext。

这是来自 pre-DbContext 工具的旧版本。 DbContext 是托管代码,它自己乐观地维护数据库连接。为什么要大锤呢?有什么急事?为什么不让垃圾收集器决定在机器空闲或需要内存时清理的最佳时间?也可以参考这个帖子:https://blog.jongallant.com/2012/10/do-i-have-to-call-dispose-on-dbcontext/

不必担心不得不处置将简化和优化您的代码。通常,我可能会从数据库“帮助程序”类继承到我使用 getter 返回已经存在的 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;
    

【讨论】:

以上是关于是否需要处理实体框架上下文对象的主要内容,如果未能解决你的问题,请参考以下文章

实体框架对象上下文刷新

如何使用实体框架关联来自多个上下文的对象

在实体框架中将对象树附加到对象上下文

如何使用对象上下文在实体框架中使用批量插入?

使用实体框架核心生成和访问存储过程

实体框架和 ASP.NET 的最佳实践