【问题标题】:Closing a conneciton in the "unload" method在“卸载”方法中关闭连接
【发布时间】:2011-07-11 11:04:42
【问题描述】:

我继承了一个 web 框架,之前的开发人员在页面生命周期的 init/unload 方法中打开和关闭了他的数据库连接。构造函数本质上是这样的(简化来说明点);

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();
      }
   }
}

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

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

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

干杯,

米奇

编辑:在调试中,卸载方法总是被调用,即使有异常。我真的只需要知道不会调用此方法的任何场景,这样我就可以确定这是否是我需要首先重构的部分。

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

【问题讨论】:

    标签: c# asp.net database-connection connection-pooling page-lifecycle


    【解决方案1】:

    我不知道这是否安全,但我浏览了 System.Web.UI.Page 类的源代码,卸载事件由私有 ProcessRequestCleanup() 触发,除非请求是异步的或跨页请求。对清理方法的调用位于 finally 块内,该块与围绕 ProcessRequest 的 try 块耦合。进程请求正在触发从 PreInit 到 Render 的所有页面生命周期事件。这意味着 Unload 将始终被触发(异步和跨页面情况除外),即使发生异常也是如此。

    但是,由于没有完全记录卸载的行为,因此在我的页面中包含此代码会让我感到非常不安。

    【讨论】:

      【解决方案2】:

      我总是使用下面的block soemthing

      using( SqlConnection)
      {
      
      }
      

      这样它就不会造成任何问题

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

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

      在你的类文件中

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

      所以通过上述方式,您不需要一次又一次地编写代码,也不需要编写代码来关闭连接,因为它会通过使用块自动处理。

      【讨论】:

      • 干杯,这正是我通常做的事情(几乎可以肯定,一旦我的问题得到解答,我最终会做的事情)。我只需要特别知道我是否应该专注于这个特定场景(卸载场景)。
      【解决方案3】:

      一个(非常)快速测试(VS2010 Web 服务器,.net v4)向我展示了 Unload 事件 is 在引发未处理的异常(至少在 Page_Load 中引发)时调用,所以这个原理看起来应该可行。

      如果连接打开,示例中列出的模式仅是dispose'ing 连接。

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

      1. 直接致电_conn.Close()。如果连接未打开,则不会在 EndConnection 中进行配置。

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

      考虑更改您的 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 实例。

      【讨论】:

        【解决方案4】:

        编辑:根据 PHeiberg 的回答,这在 .net 4 中绝对是可能的,并且可以假设总是会调用 unload。

        我也检查了 .net 2.0 代码,那里也是如此。

        【讨论】:

        • 这不是真的(见其他 cmets)
        【解决方案5】:

        没有。举个例子。

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

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

        【讨论】:

        • 卸载方法是服务器端的,所以一旦收到请求,用户做什么肯定不重要吗?
        • "对于页面本身,使用此事件来完成最后的清理工作,例如关闭打开的文件和数据库连接,或者完成日志记录或其他特定于请求的任务。"来自msdn.microsoft.com/en-us/library/ms178472.aspx。因此,您可能正在做某事,但是如果您再次查看我帖子中的 Unload 链接,您会发现它并不总是被调用。如果可能的话,我不会指望它被调用,而是按照 Pranay Rana 的建议去做,这将是处理你的连接的正确方法。
        • 这里还有一个有趣的 SO stackoverflow.com/questions/302149/… 链接,可能“超时已过期”是由其他原因引起的,尝试使用 SQL Profiler 查看后台发生的情况。
        • 为这个人干杯,我要坐一会儿,看看我是否得到更多回应——如果卸载事件,不想花时间实现 using() 模式只是在做同样的事情。
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2013-12-04
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2023-03-15
        • 2013-10-14
        • 2016-02-25
        相关资源
        最近更新 更多