即使从未调用TransactionScope.Complete(),为什么提交嵌套事务?

时间:2011-05-31 12:01:19

标签: sql-server-2008 transactions transactionscope system.transactions

我正在测试嵌套交易是如何工作的,并发现了这种令人不安和意外的行为。

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。

2 个答案:

答案 0 :(得分:6)

首先,有no such thing as a nested transaction in SQL Server。这很重要。

其次,两个TransactionScope都使用conn1,因此您(在SQL Server级别)为每个@@TRANCOUNT递增BEGIN TRANSACTION

简单说明:当外部事务提交时内部事务被提交,因为回滚内部会回滚两个事务

COMMIT TRANSACTION(隐含.Complete.Dispose)递减@@TRANCOUNTROLLBACK TRANSACTION(仅隐含.Dispose)接受它回到零。因此,内部回滚被抑制,因为“没有嵌套事务这样的东西”

如果您在内部'范围内正确使用了conn2,它将按预期工作,因为在数据库服务器级别上2个事务是不相关的。哪个是重要的......

答案 1 :(得分:0)

你的第二个Command对象是在conn1上创建的,而不是conn2,所以它与另一个问题非常相似 - 在第二个事务范围之前打开了运行命令的连接被打开了。