1

我正在测试以了解嵌套事务的工作原理,并发现了这种令人不安和意外的行为。

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

我看到了另一个问题,但解决方案不适用。

如果我不指定TransactionScopeOption.RequiresNew(即我没有使用嵌套事务,只是一个嵌套作用域),那么当内部作用域没有完成时整个事务会回滚,调用otx.Complete时会出错()。这可以。

但我当然不希望嵌套事务在未成功完成时被提交!有谁知道这里发生了什么以及我如何获得预期的行为?

数据库是 SQL Server 2008 R2。

4

2 回答 2

6

首先,在 SQL Server 中没有嵌套事务之类的东西。这个很重要。

其次,两个 TransactionScopes 都使用 conn1 所以你(在 SQL Server 级别)@@TRANCOUNT为每个递增BEGIN TRANSACTION

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

也就是说,COMMIT TRANSACTION(由.Complete和暗示.Dispose)递减@@TRANCOUNT,而ROLLBACK TRANSACTION.Dispose仅由 暗示)将其恢复为零。所以内部回滚被抑制了,因为“没有嵌套事务”

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

于 2011-05-31T12:26:45.127 回答
0

您的第二个 Command 对象是在conn1, not上创建的conn2,因此它与另一个问题非常相似 - 您在其上运行命令的连接是在打开第二个事务范围之前打开的。

于 2011-05-31T12:26:06.670 回答