appl是否会在 GC 后(长时间后)最终关闭未正确关闭的 DBConnection?

applWill a not-properly-closed DBConnection be finally closed after GC (after long time)?

我有遵循不良设计模式的遗留代码(VB6 表单)。它在构造函数中连接到数据库,并在 class 的析构函数(即 VB6 的 Class_Terminate)中关闭它。

有数百个 class 遵循这种模式(或使用 class 遵循这种模式)。

我们现在正在将其迁移到 .NET 并遇到了问题。因为当ADO迁移到ADO.NET时(这是硬性要求),在Finalize方法中关闭ADO.NET连接会出现异常。

(更多解释:异常是:InvalidOperationException: handle is not initialized。观察连接对象,状态仍然是打开的。从以前在Whosebug的问题中,人们的建议是在使用后立即打开和关闭连接,并且不要在 class 对象的整个生命周期内保持连接打开。)

我搜索了一下,发现在.NET中,只有非托管资源才能在Finalize中释放。 DBConnection 等对象不得在 Finalize 方法中关闭。

这是一个相当尴尬的情况。目前对我们来说最好的方法显然是不要在使用后关闭每个连接,并在使用前重新打开它(这有点耗时)。我们实际上是在考虑忽略Finalize方法中Close过程中的异常。

我想问一下,

1) ADO.Net 中的 DBConnection 是否实现了 Finalize 并会在 GC 期间关闭真正的底层连接?如果这是真的,那么在 Finalize 中忽略关闭异常)不会真正对我们造成伤害。

2) 如果不是,底层连接(可能来自连接池?)最终会返回到系统或连接池吗?说系统或连接池会在一段时间后检查异常连接状态并取回资源?

谢谢。

1) Does DBConnection in ADO.Net implements a Finalize and will close the real underlying connection during GC? If this is true, then ignore close exception in Finalize) won't really do harm to us.

2) If not, will the underlying connections (maybe from connection pool?) be finally returned back to system or connection pool? Say the system or connection pool will check abnormal connection states and retrieve back the resources after some long time?

  1. 垃圾收集器将只收集托管代码。这就是为什么我们有 IDisposable 接口。
    底层连接不是托管代码,因此不会被垃圾收集器关闭。

  2. 连接池将在 x 次关闭且未使用后关闭底层连接。绑定到 IDbConnection 打开实例的数据库的 "real" 连接将不可用于连接池发出或关闭。

将这两个事实结合在一起,您很快就会明白,在 class 级别保持打开的连接不仅可以防止它被垃圾收集器收集,只要您对它有一个积极的引用class,还能防止连接池杀死底层连接。
这导致了评论中已经提到的许多问题。

我知道你有很多 classes 像这样连接到数据库(顺便说一句,即使在 VB6 和 ADO 时代,这也是一个不好的做法) - 所以改变每个 class 手动是不可行的。

但是,您可以创建自己的 classes 以在应用程序代码和 ADO.Net 代码之间添加一个层,而不是将 ADO 直接映射到 ADO.Net,而是将其映射到你自己的 class.

所以基本上你会有一个实现 IDbConnection 接口的连接 class 和一个实现 IDbCommand 接口的命令 class。

在您的 IDbCommand 实现中,您应该在执行命令方法时打开和关闭实际连接。

这样一来,您将旧代码映射到新代码的工作量相对较少,并且仅在您实际需要时才使用连接的所有好处。