是否的SqlTransaction需要有所谓的处置?SqlTransaction

2023-09-03 16:45:29 作者:月下红烛泪

我需要调用部署在最后阻塞的SqlTransaction? pretend开发人员没有使用任何地方使用,并刚的try / catch。

 的SqlTransaction sqlTrans = con.BeginTransaction();

尝试
{
     //做工作
sqlTrans.Commit()
}
赶上(例外前)
        {

           sqlTrans.Rollback();
        }

 最后
        {
            sqlTrans.Dispose();
            con.Dispose();
        }
 

解决方案

要重复Brians评论:它不会伤害有它。这适用于每一个类实现 IDisposable的,否则将不实现此接口。

但通常情况下,垃圾收集器将处理它,如果对象不再被引用。因为我也不想叫处理每第二个变量或使用的使用语句无处不在,它是永远值得寻找到的类处置方法的实际执行情况。

c 中SqlTransaction 事务详解

的SqlTransaction。处置

 保护覆盖无效的Dispose(BOOL处置)
{
    如果(处置)
    {
        SNIHandle目标= NULL;
        RuntimeHelpers prepareConstrainedRegions()。
        尝试
        {
            目标= SqlInternalConnection.GetBestEffortCleanupTarget(this._connection);
            如果(this.IsZombied&安培;!&安培;!this.IsYukonPartialZombie)
            {
                this._internalTransaction.Dispose();
            }
        }
        赶上(OutOfMemoryException异常E)
        {
            this._connection.Abort(E);
            扔;
        }
        赶上(StackOverflowException E2)
        {
            this._connection.Abort(E2);
            扔;
        }
        赶上(ThreadAbortException E3)
        {
            this._connection.Abort(E3);
            SqlInternalConnection.BestEffortCleanup(目标);
            扔;
        }
    }
    base.Dispose(处置);
}
 

如果不了解全部(或任何东西),这里发生了什么,我可以说,这是比简单的 base.Dispose(处置)。因此,它可能是一个好主意,以确保的SqlTransaction得到处理。

但因为 SqlConnection.BeginTransaction 创建的交易也有可能是一个好主意的反映这也:

 公开的SqlTransaction的BeginTransaction(ISO的IsolationLevel,串transactionName)
{
    SQLStatistics中的统计数据= NULL;
    字符串= ADP.IsEmpty(transactionName)? 无:transactionName;
    IntPtr的IntPtr的;
    Bid.ScopeEnter(出IntPtr的,< sc.SqlConnection.BeginTransaction | API>%D#,异=%D {ds​​.IsolationLevel},transactionName =%1!'\ N,this.ObjectID,(INT)ISO,一个);
    的SqlTransaction结果;
    尝试
    {
        统计= SqlStatistics.StartTimer(this.Statistics);
        的SqlTransaction的SqlTransaction = this.GetOpenConnection()BeginSqlTransaction(ISO,transactionName)。
        GC.KeepAlive(本);
        结果=的SqlTransaction;
    }
    最后
    {
        Bid.ScopeLeave(REF IntPtr的);
        SqlStatistics.StopTimer(统计);
    }
    返回结果;
}
 

正如你所看到的。该 GC 也将保持连接活动创建一个交易时。它也未持有参考交易,因为它只返回。因此,它即使在已经被设置在连接可能不被设置。另一种说法处置交易。

您可能也有看 的TransactionScope 类这是比较失败的,安全比的BeginTransaction 。看一看this问题获取更多的信息。

Do I need to call dispose in the finally block for SqlTransaction? Pretend the developer didnt use USING anywhere, and just try/catch.

SqlTransaction sqlTrans = con.BeginTransaction();

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

           sqlTrans.Rollback();
        }

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

解决方案

To repeat Brians comment: it does not hurt to have it. This is true for every class implementing IDisposable, otherwise it would not implement this interface.

But normally the garbage collector would deal with it if the object is not referenced anymore. Because i also don't want to call dispose on every second variable or use the using-statement everywhere, it it's always worth to look into the actual implementation of the class' Dispose method.

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

Without understanding all(or anything) what is happening here i can say that this is more than a simple base.Dispose(disposing). So it might be a good idea to ensure that a SqlTransaction gets disposed.

But because SqlConnection.BeginTransaction creates the transaction it could also be a good idea to reflect this also:

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

As you can see. The GC will also keep the Connection alive when a Transaction is created. It also don't hold a reference to the transaction since it only returns it. Hence it might not be disposed even when the connection is already disposed. Another argument to dispose the transaction.

You might also have a look at the TransactionScope class which is more fail-safe than BeginTransaction. Have a look at this question for more informations.