我正在测试嵌套交易是如何工作的,并发现了这种令人不安和意外的行为。
using(TransactionScope otx = new TransactionScope())
using(SqlConnection conn1 = new SqlConnection("Server=S;Database=DB;Trusted_Connection=yes"))
using(SqlCommand cmd1 = conn1.CreateCommand())
{
conn1.Open();
cmd1.CommandType = CommandType.Text;
cmd1.CommandText = "INSERT INTO FP.ACLs (ChangeToken,ACL) VALUES (1,0x)";
cmd1.ExecuteNonQuery();
using(TransactionScope itx = new TransactionScope(TransactionScopeOption.RequiresNew))
using(SqlConnection conn2 = new SqlConnection("Server=S;Database=DB;Trusted_Connection=yes"))
using(SqlCommand cmd2 = conn1.CreateCommand())
{
conn2.Open();
cmd2.CommandType = CommandType.Text;
cmd2.CommandText = "INSERT INTO FP.ACLs (ChangeToken,ACL) VALUES (2,0x)";
cmd2.ExecuteNonQuery();
// we don't commit the inner transaction
}
otx.Complete(); // nonetheless, the inner transaction gets committed here and two rows appear in the database!
}
我看到this other question,但解决方案不适用。
如果我没有指定TransactionScopeOption.RequiresNew(即我不使用嵌套事务,只是嵌套作用域),那么当内部作用域未完成时会回滚整个事务,并且在调用时会发生错误otx.Complete()。这很好。
但我当然不希望嵌套事务在未成功完成时提交!有谁知道这里发生了什么以及我如何能够获得预期的行为?
数据库是SQL Server 2008 R2。
答案 0 :(得分:6)
首先,有no such thing as a nested transaction in SQL Server。这很重要。
其次,两个TransactionScope都使用conn1,因此您(在SQL Server级别)为每个@@TRANCOUNT
递增BEGIN TRANSACTION
简单说明:当外部事务提交时内部事务被提交,因为回滚内部会回滚两个事务
即COMMIT TRANSACTION
(隐含.Complete
和.Dispose
)递减@@TRANCOUNT
而ROLLBACK TRANSACTION
(仅隐含.Dispose
)接受它回到零。因此,内部回滚被抑制,因为“没有嵌套事务这样的东西”
如果您在内部'范围内正确使用了conn2,它将按预期工作,因为在数据库服务器级别上2个事务是不相关的。哪个是重要的......
答案 1 :(得分:0)
你的第二个Command对象是在conn1
上创建的,而不是conn2
,所以它与另一个问题非常相似 - 在第二个事务范围之前打开了运行命令的连接被打开了。