我有遵循错误设计模式的旧版代码(VB6表单)。它连接到构造函数中的数据库,然后在类的析构函数(即VB6中的Class_Terminate)中将其关闭。
有数百种遵循该模式的类(或使用遵循该模式的类)。
我们现在将其迁移到.NET,并遇到了问题。因为当ADO迁移到ADO.NET(这是一个硬要求)时,在Finalize方法中关闭ADO.NET连接将导致异常。
(更多说明:异常是:InvalidOperationException:句柄未初始化。观察连接对象,状态仍为Open。从StackOverflow中的先前问题开始,人们的建议是在使用后立即打开和关闭连接,然后在整个类对象的整个生命周期中都不要保持连接打开。)
我搜索发现,在.NET中,只有非托管资源才能在Finalize中释放。诸如DBConnection之类的对象不得在Finalize方法中关闭。
这是一个非常尴尬的情况。目前,对我们来说最好的方法显然是不要在使用后关闭每个连接,而是在使用前重新打开它(这是很费时间的)。实际上,我们正在考虑忽略Finalize方法中Close期间的异常。
我想问
1)ADO.Net中的DBConnection是否实现Finalize并在GC期间关闭真正的基础连接?如果是这样,则忽略Finalize中的close异常不会对我们造成任何伤害。
2)如果没有,底层连接(也许来自连接池吗?)最终会返回到系统或连接池吗?假设系统或连接池将检查异常的连接状态并在很长一段时间后取回资源?
谢谢。
答案 0 :(得分:1)
1)ADO.Net中的DBConnection是否实现Finalize并在GC期间关闭真正的基础连接?如果是这样,则忽略Finalize中的close异常不会对我们造成任何伤害。
2)如果没有,底层连接(也许来自连接池吗?)最终会返回到系统或连接池吗?假设系统或连接池将检查异常的连接状态并在很长一段时间后取回资源?
垃圾收集器将仅收集托管代码。这就是为什么我们有IDisposable
界面的原因。
基础连接不是托管代码,因此垃圾收集器不会关闭该连接。
连接池将在x次关闭且未使用之后关闭基础连接。绑定到IDbConnection
的打开实例的数据库的“真实”连接将无法用于连接池发出或关闭。
将这两个事实结合在一起,您将很快理解,只要您对该类有一个有效的引用,在类级别上保持开放连接不仅会阻止垃圾收集器对其进行收集,而且还会阻止连接池以杀死基础连接。
这导致了注释中已经提到的许多问题。
我知道您有很多这样的类连接到数据库(顺便说一句,即使在VB6和ADO时代,这也是一种不好的做法)-因此手动更改每个类是不可行的。
但是,您可以创建自己的类以在应用程序代码和ADO.Net代码之间添加一个层,而不是将ADO直接映射到ADO.Net,而是将其映射到您自己的类。
因此,基本上,您将拥有一个实现IDbConnection接口的Connection类和一个实现IDbCommand接口的Command类。
在IDbCommand实现中,您应该在执行命令方法时打开和关闭实际的连接。
通过这种方式,您只需进行相对较少的工作即可将旧代码映射到新代码,以及仅在实际需要时使用连接的所有好处。