引发异常时,确保关闭SQL连接的正确方法是什么?

时间:2020-03-06 14:47:58  来源:igfitidea点击:

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

我特别想知道;在引发异常的情况下,我在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();
    }
}

解决方案

将数据库处理代码包装在"使用"中

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

由于我们还是在使用IDisposables。我们可以使用'using'关键字,该关键字基本上等效于在finally块中调用dispose,但看起来更好。

我猜测" _SqlConnection.State == ConnectionState.Closed"的意思是!=。

这肯定会起作用。我认为将连接对象本身包含在using语句中是更常见的做法,但是如果我们出于某种原因想要重用同一连接对象,那么拥有的东西就很好了。

不过,我们绝对应该更改的一件事是Dispose()方法。我们不应在处理中引用连接对象,因为它可能已在此时完成。我们应该改用建议的"处置"模式。

我可能建议这样做:

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

希望能对我们有所帮助:)

如图所示,将连接关闭代码放入"最终"块中。最后,在引发异常之前执行块。使用" using"块也可以,但是我发现显式的" Finally"方法更加清晰。

对于许多开发人员而言,使用语句是旧帽子,但是年轻的开发人员可能不知道这一点。

.Net Framework保留一个连接池是有原因的。相信它! :)
我们不必编写太多代码即可连接数据库并释放连接。

我们可以只使用'using'语句,并放心'IDBConnection.Release()'将为我们关闭连接。

精心设计的"解决方案"往往会导致错误的代码。简单更好。

MSDN文档使这一点很清楚...

  • Close方法将回滚任何未决的事务。然后,它释放与连接池的连接,或者如果禁用了连接池,则关闭连接。

我们可能尚未(也不想禁用)禁用连接池,因此在我们调用"关闭"后,池最终将管理连接状态。这可能很重要,因为从数据库服务器端看所有打开的连接时,我们可能会感到困惑。

  • 一个应用程序可以调用关闭多次。不会产生异常。

那么,为什么要为封闭测试费心呢?只需调用Close()。

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

这就是为什么using块导致闭合连接的原因。使用呼叫为我们处理。

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

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

看到这个问题的答案:

关闭并处置该呼叫哪个?

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

如果连接生存期与包含类的生存期相对应,则实现" IDisposable"并在" Dispose"中清理连接。

不需要尝试..最终围绕"使用",使用是尝试..最终