我需要使用
try-finally
或
using
语句来处理
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也会保持连接处于活动状态。它也不持有对事务的引用,因为它只返回它。因此,即使连接已被释放,它可能仍未被处理。这是释放事务的另一个理由。
您还可以查看
TransactionScope
类,它比
BeginTransaction
更加安全可靠。请查看
此问题获取更多信息。
sqlTrans
周围没有使用using
,那么明确调用Dispose()
不会有任何坏处。 - CᴏʀʏDispose()
方法应该检查是否已经被处理,如果是,则什么也不做。 - Cᴏʀʏ