【问题标题】:What is the proper way to ensure a SQL connection is closed when an exception is thrown?引发异常时确保关闭 SQL 连接的正确方法是什么?
【发布时间】:2010-09-13 13:29:08
【问题描述】:

我经常使用看起来像这样的模式。我想知道这是否可行,或者是否有我没有在这里应用的最佳实践。

我特别想知道;在抛出异常的情况下,我在 finally 块中的代码是否足以确保正确关闭连接?

public class SomeDataClass : IDisposable
{
    private SqlConnection _conn;

    //constructors and methods

    private DoSomethingWithTheSqlConnection()
    {
        //some code excluded for brevity

        try
        {
            using (SqlCommand cmd = new SqlCommand(SqlQuery.CountSomething, _SqlConnection))
            {
                _SqlConnection.Open();
                countOfSomething = Convert.ToInt32(cmd.ExecuteScalar());
            }
        }
        finally
        {
            //is this the best way?
            if (_SqlConnection.State == ConnectionState.Closed)
                _SqlConnection.Close();
        }

        //some code excluded for brevity
    }

    public Dispose()
    {
        _conn.Dispose();
    }
}

【问题讨论】:

  • 呃...你为什么要在关闭连接之前检查它是否已经关闭?如果你要在唯一使用它的方法中关闭它,为什么要使用类成员来存储连接?
  • spoon16 包含短语“//为简洁起见排除了一些代码”。由此,我推断这不是唯一使用它的方法。
  • 正确,这绝对是我能想到的最简单的例子。我认为由于连接池,我不需要尝试将 SqlConnection 对象存储在每个方法的范围之外。因此,只需按照其他建议使用 USING。
  • 关于“Finally”部分,(尽管已经指出根本没有必要)您需要检查对象是否为空。您不能在空对象上调用 .close

标签: c# .net sql sqlconnection


【解决方案1】:

我猜_SqlConnection.State == ConnectionState.Closed 是指!=

这肯定会奏效。我认为将连接对象本身包含在 using 语句中更为习惯,但是如果您出于某种原因想要重用相同的连接对象,那么您所拥有的就很好。

不过,您绝对应该更改的一件事是Dispose() 方法。您不应该在 dispose 中引用连接对象,因为它可能已经在此时完成。您应该改用推荐的 Dispose 模式。

【讨论】:

    【解决方案2】:

    .Net 框架维护一个连接池是有原因的。相信它! :) 不必为了连接数据库和释放连接而编写这么多代码。

    您可以只使用“using”语句并放心“IDBConnection.Release()”将为您关闭连接。

    高度复杂的“解决方案”往往会导致代码错误。简单更好。

    【讨论】:

      【解决方案3】:

      不需要 try..finally 围绕“使用”,使用 IS 是 try..finally

      【讨论】:

        【解决方案4】:

        像您展示的那样,将连接关闭代码放在“Finally”块中。 finally 块在抛出异常之前执行。使用“使用”块同样有效,但我发现显式“最终”方法更清晰。

        对于许多开发人员来说,使用语句是老生常谈,但年轻的开发人员可能不知道这一点。

        【讨论】:

        • "using" 语句对于任何熟悉 C# 的人来说都非常清楚。我目前正在专业地使用 Java,与“使用”相比,try/finally 的额外包袱是一件真正的苦差事。 “using”语句是 C# 中资源处理的惯用方式。
        • 很公平,但我发现即使我知道“使用”之类的东西是做什么的,实际查看代码也有助于我将操作可视化。另外,我不是唯一一个查看我的代码的人。
        【解决方案5】:

        请看这个问题的答案:

        Close and Dispose - which to call?

        如果您的连接生命周期是单个方法调用,请使用该语言的using 功能来确保正确清理连接。虽然try/finally 块在功能上是相同的,但它需要更多的代码并且 IMO 的可读性较差。无需检查连接状态,您可以拨打Dispose,它会处理清理连接。

        如果您的连接生命周期对应于包含类的生命周期,则实现IDisposable 并清理Dispose 中的连接。

        【讨论】:

          【解决方案6】:

          MSDN Docs 说得很清楚...

          • Close 方法回滚所有挂起的事务。然后它释放到连接池的连接,或者如果连接池被禁用,则关闭连接。

          您可能没有(也不想)禁用连接池,因此池最终会在您调用“关闭”后管理连接的状态。这可能很重要,因为从数据库服务器端查看所有打开的连接可能会让您感到困惑。


          • 应用程序可以多次调用 Close。不会产生异常。

          那么,为什么还要为 Closed 测试呢?只需调用 Close()。


          • Close 和 Dispose 在功能上是等效的。

          这就是 using 块导致连接关闭的原因。 使用为你调用 Dispose。


          • 不要在类的 Finalize 方法中对 Connection、DataReader 或任何其他托管对象调用 Close 或 Dispose。

          重要的安全提示。谢谢,埃贡。

          【讨论】:

            【解决方案7】:

            我可以建议这个吗:

            
                class SqlOpener : IDisposable
                {
                    SqlConnection _connection;
            
                    public SqlOpener(SqlConnection connection)
                    {
                        _connection = connection;
                        _connection.Open();
            
                    }
            
                    void IDisposable.Dispose()
                    {
                        _connection.Close();
                    }
                }
            
                public class SomeDataClass : IDisposable
                {
                    private SqlConnection _conn;
            
                    //constructors and methods
            
                    private void DoSomethingWithTheSqlConnection()
                    {
                        //some code excluded for brevity
                        using (SqlCommand cmd = new SqlCommand("some sql query", _conn))
                        using(new SqlOpener(_conn))
                        {
                            int countOfSomething = Convert.ToInt32(cmd.ExecuteScalar());
                        }
                        //some code excluded for brevity
                    }
            
                    public void Dispose()
                    {
                        _conn.Dispose();
                    }
                }
            

            希望有帮助:)

            【讨论】:

            • -1:不起作用! SqlCommand 仍然持有一个空引用。请不要修复它 - 只需坚持上面的标准解决方案
            • Err,我真的没有问题,所以假设代码 around 它实际上创建了一个连接,所以运行它。看,如果“_conn”没有正确初始化,当然它不会工作。
            【解决方案8】:

            因为无论如何您都在使用 IDisposables。可以使用'using'关键字,基本上相当于在finally块中调用dispose,但是看起来更好。

            【讨论】:

              【解决方案9】:

              将您的数据库处理代码包装在“使用”中

              using (SqlConnection conn = new SqlConnection (...))
              {
                  // Whatever happens in here, the connection is 
                  // disposed of (closed) at the end.
              }
              

              【讨论】:

              • 另请注意,除非您在连接字符串中专门将其关闭,否则 .NET 默认会池连接,因此您无需尝试挂起连接即可重用它。
              • 即使在块内显式抛出异常,connection 和 cmd 对象是否仍然在 using 块内关闭?
              最近更新 更多