9

我需要在 SqlTransaction 的 finally 块中调用 dispose 吗?假装开发人员没有在任何地方使用 USING,然后尝试/捕获。

SqlTransaction sqlTrans = con.BeginTransaction();

try
{
     //Do Work
sqlTrans.Commit()
}
catch (Exception ex)
        {

           sqlTrans.Rollback();
        }

 finally
        {
            sqlTrans.Dispose();
            con.Dispose();
        }
4

3 回答 3

19

我是否需要使用try-finallyusing-statement 来处理SqlTransaction?

拥有它并没有什么坏处。对于每个实现IDisposable的类都是如此,否则它不会实现这个接口。

但通常垃圾收集器处理未引用的对象(这并不意味着 GC 调用 dispose,这不是真的),因此您只需要它来处理非托管资源。但是因为我也不想调用dispose所有其他变量或到处使用using 语句,所以研究类方法的实际实现总是值得的Dispose

SqlTransaction.Dispose

protected override void Dispose(bool disposing)
{
    if (disposing)
    {
        SNIHandle target = null;
        RuntimeHelpers.PrepareConstrainedRegions();
        try
        {
            target = SqlInternalConnection.GetBestEffortCleanupTarget(this._connection);
            if (!this.IsZombied && !this.IsYukonPartialZombie)
            {
                this._internalTransaction.Dispose();
            }
        }
        catch (OutOfMemoryException e)
        {
            this._connection.Abort(e);
            throw;
        }
        catch (StackOverflowException e2)
        {
            this._connection.Abort(e2);
            throw;
        }
        catch (ThreadAbortException e3)
        {
            this._connection.Abort(e3);
            SqlInternalConnection.BestEffortCleanup(target);
            throw;
        }
    }
    base.Dispose(disposing);
}
        

在不了解这里发生的所有(或任何事情)的情况下,我可以说这不仅仅是一个简单的base.Dispose(disposing). 因此,确保处理 SqlTransaction 可能是一个好主意。

但是因为创建了事务,所以反映SqlConnection.BeginTransaction这一点也可能是一个好主意:

public SqlTransaction BeginTransaction(IsolationLevel iso, string transactionName)
{
    SqlStatistics statistics = null;
    string a = ADP.IsEmpty(transactionName) ? "None" : transactionName;
    IntPtr intPtr;
    Bid.ScopeEnter(out intPtr, "<sc.SqlConnection.BeginTransaction|API> %d#, iso=%d{ds.IsolationLevel}, transactionName='%ls'\n", this.ObjectID, (int)iso, a);
    SqlTransaction result;
    try
    {
        statistics = SqlStatistics.StartTimer(this.Statistics);
        SqlTransaction sqlTransaction = this.GetOpenConnection().BeginSqlTransaction(iso, transactionName);
        GC.KeepAlive(this);
        result = sqlTransaction;
    }
    finally
    {
        Bid.ScopeLeave(ref intPtr);
        SqlStatistics.StopTimer(statistics);
    }
    return result;
}

如你看到的。GC也会在创建 Transaction 时保持 Connection 处于活动状态。它也不包含对事务的引用,因为它只返回它。因此,即使连接已经被释放,它也可能不会被释放。处置交易的另一个论据。

您可能还会TransactionScope查看BeginTransaction. 查看此问题以获取更多信息。

于 2012-03-01T23:18:23.683 回答
8

在一般情况下,IDisposable您构建或获得和拥有的每个对象都必须处理掉。

在特定情况下,有一些例外,但SqlTransaction不是其中之一。

根据以下文档SqlTransaction.Dispose

释放DbTransaction 使用的非托管资源,并可选择释放托管资源。

(我的重点)

由于文档没有说明在您发出提交或回滚时释放了这些非托管资源,因此您需要处置此对象。

于 2012-03-01T23:12:22.517 回答
1

我认为你可以关闭连接。我检查了 BCL 的代码——似乎连接处理了它的事务——不需要明确地关闭它。

于 2012-03-01T23:16:42.577 回答