我应该将实体框架视为非托管资源吗?

时间:2015-08-05 13:13:04

标签: c# entity-framework destructor idisposable

我正在使用一个在其构造函数中使用EF引用的类。

我已经实现了IDisposable,但我不确定是否需要析构函数,因为我不确定我是否可以将EF归类为非托管资源。

如果EF是托管资源,那么我不需要析构函数,所以我认为这是一个恰当的例子:

public ExampleClass : IDisposable
{
    public ExampleClass(string connectionStringName, ILogger log)
    {
        //...
        Db = new Entities(connectionStringName);
    }

    private bool _isDisposed;

    public void Dispose()
    {
        if (_isDisposed) return;

        Db.Dispose();

        _isDisposed= true;
    }
}

如果EF不受管理,那么我将继续这样做:

public ExampleClass : IDisposable
{
    public ExampleClass(string connectionStringName, ILogger log)
    {
        //...
        Db = new Entities(connectionStringName);
    }

    public void Dispose()
    {
        Dispose(true);
    }

    ~ExampleClass()
    {
        Dispose(false);
    }

    private bool _isDisposed;

    protected virtual void Dispose(bool disposing)
    {
        if (_isDisposed) return;

        // Dispose of managed resources
        if (disposing)
        {
            // Dispose of managed resources; assumption here that EF is unmanaged.
        }
        // Dispose of unmanaged resources
        Db.Dispose();

        _isDisposed = true;
        //freed, so no destructor necessary.
        GC.SuppressFinalize(this);

    }
}

是哪一个?

2 个答案:

答案 0 :(得分:8)

在这种情况下,你永远不会想要使用终结器(析构函数)。

DbContext是否包含非托管资源,甚至是否负责任地释放这些非托管资源,与您是否可以尝试从终结器调用DbContext.Dispose()无关。

事实是,只要您拥有托管对象(DbContext的实例是), 从不 安全尝试调用该实例上的任何方法。原因是,在调用终结器时,DbContext对象可能已经被GC收集并且不再存在。如果发生这种情况,您在尝试拨打NullReferenceException时会收到Db.Dispose()。或者,如果你很幸运,并且Db仍然是#34;还活着",如果它依赖于其他对象,那么也可以从DbContext.Dispose()方法中抛出异常。已经定稿并收集。

正如"Dispose Pattern" MSDN article所说:

  

X DO NOT 访问终结器代码路径中的任何可终结对象,因为它们已经完成的风险很大。

     

例如,具有对另一个可终结对象B的引用的可终结对象A不能在A的终结器中可靠地使用B,反之亦然。终结器以随机顺序调用(缺少关键终结的弱排序保证)。

另外,请注意以下Eric Lippert的When everything you know is wrong, part two

  

神话:终结者以可预测的顺序运行

     

假设我们有一个对象树,所有对象都可以终结,并且都在终结器队列中。从树根到树叶,从树叶到根,或任何其他顺序,都没有要求树最终确定。

     

神话:正在最终确定的对象可以安全地访问另一个对象。

     

这个神话直接来自前一个。如果你有一个对象树并且你正在最终确定根,那么这些孩子仍然活着 - 因为它是活着的,因为它在终结队列中,所以孩子们有生活参考 - 但孩子们可能已经已经完成,并且没有特别好的状态来访问他们的方法或数据。

还有其他需要考虑的因素:你想要处理什么?您是否关注确保数据库连接及时关闭?如果是这样,那么您对EF documentation对此有何看法感兴趣:

  

默认情况下,上下文管理与数据库的连接。上下文根据需要打开和关闭连接。例如,上下文打开一个连接来执行查询,然后在处理完所有结果集后关闭连接。

这意味着,默认情况下,连接不需要DbContext.Dispose()来及时关闭。在执行查询时,它们(从连接池)打开和关闭。因此,尽管确保您始终明确地致电DbContext.Dispose()仍然是一个非常好的主意,但如果您不这样做或因某些原因忘记了,那么知道它是有用的。 ,默认情况下,这不会导致某种连接泄漏。

最后,您可能想要记住的最后一件事是,您发布的代码没有终结器,因为您在另一个类的构造函数中实例化了DbContext,实际上有可能DbContext.Dispose()方法永远不会被调用。很高兴知道这个特殊情况,所以你不会被裤子弄下来。

例如,假设我稍微调整了一下代码,以便在实例化DbContext的构造函数中 之后 抛出异常

public ExampleClass : IDisposable
{
    public ExampleClass(string connectionStringName, ILogger log)
    {
        //...
        Db = new Entities(connectionStringName);

        // let's pretend I have some code that can throw an exception here.
        throw new Exception("something went wrong AFTER constructing Db");
    }

    private bool _isDisposed;

    public void Dispose()
    {
        if (_isDisposed) return;

        Db.Dispose();

        _isDisposed= true;
    }
}

让我们说你的课程是这样用的:

using (var example = new ExampleClass("connString", log))
{
    // ...
}

即使这看起来是一个非常安全和干净的设计,因为在<{em> 之后ExampleClass 的构造函数中抛出异常{{em> {1}}已经创建,永远不会调用DbContext,并且在新创建的实例上永远不会调用ExampleClass.Dispose()

您可以详细了解这种不幸情况here

要确保始终调用DbContext.Dispose() DbContext方法,无论Dispose()构造函数内发生什么,您都必须修改ExampleClass }类到这样的东西:

ExampleClass

但是如果构造函数不仅仅是创建public ExampleClass : IDisposable { public ExampleClass(string connectionStringName, ILogger log) { bool ok = false; try { //... Db = new Entities(connectionStringName); // let's pretend I have some code that can throw an exception here. throw new Exception("something went wrong AFTER constructing Db"); ok = true; } finally { if (!ok) { if (Db != null) { Db.Dispose(); } } } } private bool _isDisposed; public void Dispose() { if (_isDisposed) return; Db.Dispose(); _isDisposed= true; } } 的实例,那么上面只是一个问题。

答案 1 :(得分:0)

C#提供垃圾回收,因此不需要显式的析构函数。但是,如果您确实控制了非托管资源,则在使用完该资源后,您将需要显式释放该资源。对该资源的隐式控制由Finalize()方法(称为终结器)提供,当对象被破坏时,垃圾收集器将调用该方法。

https://www.oreilly.com/library/view/programming-c/0596001177/ch04s04.html