在自定义 class 中为 Entity Framework 实施 IDisposable

Implementing IDisposable for Entity Framework in custom class

我用 Entity Framework 喜欢

public sealed class CacheManagementHelper 
{
   private readonly GJobEntities db = new GJobEntities();

   public List<User> GetUsers()
   { 
       return db.Users.ToList();
   }
}

并且 MS Visual Studio 2019 建议关注

Warning CA1001 Implement IDisposable on 'CacheManagementHelper' because it creates members of the following IDisposable types: 'GJobEntities'. If 'CacheManagementHelper' has previously shipped, adding new members that implement IDisposable to this type is considered a breaking change to existing consumers.

我在这里找到了一些线索

Entity Framework and calling context.dispose()

但是还不清楚我有没有实现IDisposable

感谢帮助!

But it is still unclear if I have implement IDisposable.

这是一个有几个答案的棘手问题:

  • 不,你不应该。
  • 是的,你应该。
  • 不,您不必这样做。

总的来说: IDisposable 对象应该在其生命周期结束时被释放。关于框架是否为您处理这个问题存在一些争论,但这是一种错误的方法。 (在底部,在 错误的方法 下,我将添加原因)

销毁的责任一般在创作者。所以; 如果你创建它,你就处置它

@swdon 的评论就在这里;

The biggest issue I see in his code is db is public. I don't know who is responsible for getting rid of correctly. – swdon Aug 2 '19 at 7:07

这基本上就是为什么你不应该 public 非只读 IDisposable 成员字段。因为:当调用者覆盖它时,您将没有简单的方法来跟踪它,然后谁负责调用 Dispose?


不,你不应该

国际奥委会

因此,您可能想要使用 IoC framework like Unity。这个 IoC 框架所做的是;它接管了创建对象的责任,因此,它也负责对象的处置。请注意:IoC 框架的目的不是成为工厂,它只是副作用之一。

因此,使用 IoC,您不会创建 自己的成员,您可以请求它。它可能看起来像这样:

private GJobEntities _context;

//constructor: this object can also be created by the IoC framework.
public CacheManagementHelper (GJobEntities context)
{
    //set your field here
    _context = context;
}

如果你使用这个,你不必在这里实现 IDispossable 因为你不是自己创建对象,你只是从框架请求上下文并让它调用 Dispose方法。

为什么要提这个?因为它是 the future of .net.

的默认值

所以,使用这种方法;

不,你不应该实现 IDisposable,因为你没有创建 IDisposables,(但你应该摆脱 DbContext 的创建)。


是的,你应该

IDisposable

如果您需要坚持当前的模式 - 这是有道理的 - 您应该实施 IDisposable。通过创建 IDispossable 成员,您负责处置它们。您可以通过实施 IDisposable.

将其委托给 class 的创建者
public sealed class CacheManagementHelper : IDisposable

此对象的创建者现在创建了 IDisposable 对象,因此负责处置它。

您唯一的工作就是正确实现接口并释放您的资源。

通过阅读文档正确实现它是很不错的。我建议您使用此实现:Proper use of the IDisposable interface

所以,使用这种方法;

是的,您应该实施 IDisposable 并清理您的资源。


不,你不必

或者,

您可以在调用函数时创建上下文,将其保存在本地并将其放置在那里。通过这种方式,您将克服实现 IDisposable 的需要,因为上下文的创建者也在处理它。

//keeping it local
public List<User> GetUsers()
{ 
    using (var db = new GJobEntities())
        return db.Users.ToList();
}

所以,使用这种方法;

不,您不必实施 IDisposable,因为您没有创建 IDisposable 成员字段。


错误的方法

如果您阅读“Proper use of the IDisposable interface”,您就会知道,如果正确实施,您就可以安全地防止内存泄漏。

因此,有人可能会争辩说您 不需要 一起调用 Dispose,因为垃圾收集器会为您完成。

这是一种误解。

问题如下:

  • 1) 你不知道你调用的组件是否正确实现了。如果不处理它可能会泄漏(如果使用无人管理的内存)。
  • 2)内存泄漏是;时间有关。惊奇咦?这样看:当你重新启动系统或重新启动应用程序时,内存又回来了。它不是永久性的。从过去开始,这就与应用程序的生命周期有关。但是关于多线程、无服务器编码等方面的发展确实需要我们稍微不同地看待它。 泄漏可能在特定时间范围内相关。一个很好的例子是高内存消耗图像处理或耗尽连接池。

所以,没有办法告诉垃圾收集器什么时候来清理东西。它可能不够快,无法在以下方面产生问题:打开的连接数、图形资源等。你正在泄漏,并依赖垃圾收集器来修复你的混乱,这是一种错误的方法。


最后的评论: EF DbContext 通过跟踪您的所有更改(它基本上是一个工作单元)在其生命周期内收集了大量资源。随着时间的推移,保持长期存在的 DbContext 可能会导致性能下降。所以,请注意这一点。

您必须通过实施 IDisposable 接口向消费者提供 class 处理内部 db 上下文的可能性:

public sealed class CacheManagementHelper : IDisposable 
{
   private readonly GJobEntities db = new GJobEntities();

   public List<User> GetUsers()
   { 
       return db.Users.ToList();
   }

   public void Dispose()
   { 
       db.Dispose();          
   }
}

您还可以实施Finalizer以确保资源将被释放,即使消费者端没有任何代码更改。