gpt4 book ai didi

c# - 我应该将 Entity Framework 视为非托管资源吗?

转载 作者:太空狗 更新时间:2023-10-29 20:16:31 26 4
gpt4 key购买 nike

我正在使用一个在其构造函数中使用对 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);

}
}

是哪一个?

最佳答案

在这种情况下,您绝对不想使用终结器(析构函数)。

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

事实是,任何时候您有一个托管对象(DbContext 的一个实例),从不尝试调用任何方法都是安全的在那个例子上。原因是,在调用终结器时,DbContext对象可能已经被 GC 收集并且不再存在。如果发生这种情况,您将得到 NullReferenceException。尝试调用 Db.Dispose() 时.或者,如果你幸运的话,Db仍然“活着”,也可以从 DbContext.Dispose() 中抛出异常方法,如果它依赖于已经完成和收集的其他对象。

因为这个"Dispose Pattern" MSDN article说:

X DO NOT access any finalizable objects in the finalizer code path, because there is significant risk that they will have already been finalized.

For example, a finalizable object A that has a reference to another finalizable object B cannot reliably use B in A’s finalizer, or vice versa. Finalizers are called in a random order (short of a weak ordering guarantee for critical finalization).

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

Myth: Finalizers run in a predictable order

Suppose we have a tree of objects, all finalizable, and all on the finalizer queue. There is no requirement whatsoever that the tree be finalized from the root to the leaves, from the leaves to the root, or any other order.

Myth: An object being finalized can safely access another object.

This myth follows directly from the previous. If you have a tree of objects and you are finalizing the root, then the children are still alive — because the root is alive, because it is on the finalization queue, and so the children have a living reference — but the children may have already been finalized, and are in no particularly good state to have their methods or data accessed.


需要考虑的其他事项:您要处理什么?您是否关心确保及时关闭数据库连接?如果是这样,那么您会对 EF documentation 的内容感兴趣。不得不说:

By default, the context manages connections to the database. The context opens and closes connections as needed. For example, the context opens a connection to execute a query, and then closes the connection when all the result sets have been processed.

这意味着,默认情况下,连接不需要 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))
{
// ...
}

尽管这看起来是一个非常安全和干净的设计,因为在 ExampleClass 的构造函数中抛出了一个异常。 DbContext 的新实例之后已经创建,ExampleClass.Dispose()永远不会被调用,并且推而广之,DbContext.Dispose()永远不会在新创建的实例上调用。

您可以阅读更多关于这种不幸情况的信息 here .

确保DbContextDispose()方法总是被调用,无论 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;
}
}

但是如果构造函数不仅仅是创建一个 DbContext 的实例,那么以上内容实际上只是一个问题。 .

关于c# - 我应该将 Entity Framework 视为非托管资源吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/31833599/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com