在“卸载”方法中关闭连接

我inheritance了一个Web框架,以前的开发人员在页面生命周期的init / unload方法中打开并关闭了他的数据库连接。 基本上构造函数是这样的(简化以certificate这一点);

public class BasePage { protected DBConnection _conn; public BasePage() { Init += StartConnection; Unload += EndConnection; } private void StartConnection(object sender, EventArgs e) { _conn = new DBConnection(Application["connectionstring"].ToString()); } private void EndConnection(object sender, EventArgs e) { if (_conn == null) return; if (_conn.Connection.State == ConnectionState.Open) { _conn.Close(); _conn.Dispose(); } } } 

自从我来到这里以来,发展一直很快,所以我从未停下来考虑它。 最近,访问已经开始,我们已经开始获得可怕的“Timeout过期。在从池中获取连接之前已经过了超时时间……”错误。

我目前正在查看其余的代码,寻找可能的连接泄漏,但上面的代码从来没有完全适合我,我想消除它作为潜在的罪魁祸首。 那么问题;

即使在发生exception的情况下,我是否可以依赖“卸载”方法始终被调用? 或者任何人都可以使用上述模式看到任何其他潜在的问题,这将使它成为这些连接泄漏的主要嫌疑人?

干杯,

米奇

编辑:在调试中,即使存在exception,也始终会调用unload方法。 我真的只需要知道不会调用此方法的任何情况,所以我可以弄清楚这是否需要首先进行重构。

编辑:感谢那些迄今为止做出回应的人,但请不要再提出关于IDisposable类或“使用”或“捕获/最终”模式的建议 – 这不是我的问题! 我的问题是具体是一个页面是否可以运行其“Init”事件但是然后无法运行是“卸载”事件,以及为什么会发生这种情况。

我没有关于这是否安全的确切知识,但我通过System.Web.UI.Page类的源代码进行了调查,并且私有ProcessRequestCleanup()触发了unload事件,除非请求是异步或跨页请求。 对清理方法的调用是在finally块内部,该块耦合到围绕ProcessRequest的try块。 进程请求正在触发从PreInit到Render的所有页面生命周期事件。 这意味着即使发生exception,也会始终触发卸载(异步和跨页情况除外)。

但是,由于卸载的行为并未完全记录,因此在我的页面中使用此代码会感到非常不安。

我总是使用如下所示的块soemthing

 using( SqlConnection) { } 

这样它就不会引起任何问题

如果您不想再次编写打开连接的代码,请再次创建一个类

 public class SqlConnectionManager { public SqlConnection GetSqlConnectionManager() { //create and return connection //SqlConnection con = new SqlConnection(); //return con; } } 

在You类文件中

 SqlConnection conn = null; using (conn = (new SqlConnectionManager()).GetSqlConnectionManager()) { //do work with connection } 

因此,通过上面的方式,您不需要反复编写代码,也不需要编写代码来关闭连接,因为它会通过使用块自动进行处理。

(非常)快速测试(VS2010 Web服务器,.net v4)向我展示了在引发未处理的exception时调用Unload事件(至少在Page_Load中引发),因此原理看起来应该有效。

示例中列出的模式仅在连接打开时才dispose连接。

由于_conn受到保护,因此来自BasePage的页面可以与_conn交互并修改其值。 后代类有两种方法可以打破模式:

  1. 直接调用_conn.Close() 。 如果连接未打开,则不会将其置于EndConnection中。

  2. 通过将_conn设置为null或为其分配新的DBConnection实例来修改_conn的值。

考虑更改EndConnection方法,以便始终处理_conn。

 private void EndConnection(object sender, EventArgs e) { if (_conn == null) { return; } if (_conn.Connection.State == ConnectionState.Open) { _conn.Close(); } _conn.Dispose(); // always dispose even if not actually open. It may have been closed explicitly elsewhere. } 

EndConnection无法捕获案例2。 考虑将_conn私有并提供getter属性:

 private DBConnection _conn; protected DBConnection Connection { get { return _conn; } } 

这可以防止后代类更改_conn的值。

最后,DBConnection是你自己的一类吗? 我只问你引用“_conn.Connection.State”而不仅仅是_conn.State。 如果是这样,只需仔细检查DBConnection的Dispose方法是否正确配置其Connection实例。

编辑:根据PHeiberg的回答,这绝对可以在.net 4中进行,并且可以假设将始终调用卸载。

我也检查了.net 2.0代码,同样也是如此。

不。请参考以下示例。

如果用户关闭浏览器会发生什么? 然后将不会调用卸载function,您将与数据库建立打开连接。

StackOverflow上的这个卸载问题与您遇到的问题类似。