假设您按照Microsoft example here设置了一个TransactionScope对象。现在假设您需要更新许多数据库表,并且您希望它们都在TransactionScope对象的范围内。不断地嵌套SqlConnection和SqlCommand对象10深会产生源代码混乱。 如果您调用其他创建连接的函数(例如,在您的数据访问层中),它们是否在TransactionScope对象的范围内?
示例:
' Assume variable "x" is a business object declared and populated with data.
Using scope As New TransactionScope()
Dal.Foo.SaveProducts(x.Products)
Dal.Foo.SaveCustomer(x.Customer)
Dal.Foo.SaveDetails(x.Details)
' more DAL calls ...
Dal.Foo.SaveSomethingElse(x.SomethingElse)
scope.Complete()
End Using
假设每个DAL函数都包含自己的using
连接语句。例如:
Public Shared Sub SaveProducts(x As Object)
Using conn As New SqlConnection("connection string")
Using cmd As New SqlCommand("stored procedure name", conn)
With cmd
' etc.
End With
End Using
End Using
End Sub
答案 0 :(得分:4)
是的,它们将在TransactionScope中。 TransactionScope基本上做的是创建一个Transaction对象并将Transaction.Current设置为该对象。
换句话说,这个:
Using scope As New TransactionScope()
... blah blah blah ...
End Using
与此基本相同:
try
{
// Transaction.Current is a thread-static field
Transaction.Current = new CommittableTransaction();
... blah blah blah ...
}
finally
{
Transaction.Current.Commit(); // or Rollback(), depending on whether the scope was completed
Transaction.Current = null;
}
当打开SqlConnection时,它会检查Transaction.Current(在此线程上)是否为null,如果它不为null,则它会登记(除非在连接字符串中为enlist = false)。所以这意味着SqlConnection.Open()不知道或不关心是否在此方法中打开了TransactionScope,或者是否调用了此方法。
(注意,如果您希望子方法中的SqlConnection不在事务中,则可以使用TransactionScopeOption.Suppress创建内部TransactionScope)
答案 1 :(得分:2)
创建TransactionScope时,您在TransactionScope存在时打开的所有连接都会自动加入事务(它们是“自动登记”)。所以你不需要传递连接字符串。
当SQL Server看到不同的事务时(即使它们都包含在一个DTC事务中),您可能仍然希望它们之间不共享锁。如果你打开太多的联系并进行大量的阅读和写作,你就会陷入僵局。
为什么不将活动连接放在某个全局位置并使用它?
一些研究后的更多信息。请阅读:TransactionScope automatically escalating to MSDTC on some machines?。
如果你正在使用SQL Server 2008(可能是2012,但不是任何其他数据库),一些魔术在幕后完成,如果你打开两个SQL连接一个接一个,它们将被整合到一个SQL事务中,并且您不会遇到任何锁定问题。
然而,如果您使用的是其他数据库,或者您可能同时打开两个连接,您将获得DTC事务,这意味着SQL Server将无法正确管理锁定,您可以遇到非常不愉快和意外的僵局。
虽然很容易确保您只在SQL Server 2008上运行,但确保您不会同时打开两个连接会有点困难。很容易忘记它并做这样的事情:
class MyPersistentObject
{
public void Persist()
{
using(SQLConnection conn=...)
{
conn.Open()
WriteOurStuff()
foreach(var child in this.PersistedChildren)
child.Persist()
WriteLogMessage()
}
}
}
如果孩子的Persist方法打开另一个连接,您的交易将升级为DTC交易,并且您正面临潜在的锁定问题。
所以我仍然建议在一个地方保持连接并通过DAL使用它。它不必是一个简单的全局静态变量,您可以使用ConnectionManager.Current属性创建一个简单的ConnectionManager类,该属性将保存当前连接。将ConnectionManager.Current设为[ThreadStatic],您解决了大部分潜在问题。这就是TransactionScope在幕后的工作方式。