C# 回滚外部 TransactionScope 而不管嵌套 TransactionScopes 中发生了什么

C# Rollback Outer TransactionScope Regardless of What Happens In Nested TransactionScopes

首先,我看过这个类似的问题:Nested/Child TransactionScope Rollback但是答案没有提供解决方案,我们的问题也略有不同。

基本上,我有一个使用事务作用域的数据库集成测试(实际上,作用域是在 setup/teardown 中抽象管理的 class)

[Test]
public void MyTest()
{
    using(var outerScope = new TransactionScope())
    {
        Assert.True(_myService.MyMethod());
        var values = _myService.AnotherMethod();
    }
}

并且MyService.MyMethod也使用了TransactionScope

public bool MyMethod()
{
    using(var innerScope = new TransactionScope())
    using(var con = conFact.GetOpenConnection())
    {
        var cmd = con.CreateCommand();
        //set up and execute command
        if (isCheck) scope.Complete();
        return isCheck;
    }
}

所以理论上,如果 isCheck 为真,MyMethod 只会提交它的更改,但无论该事务是否提交,当方法被测试时,它都会被回滚。

它按预期工作,除非 isCheck 为假,在这种情况下我得到以下异常:System.Transactions.TransactionException : The operation is not valid for the state of the transaction.

我认为这里发生的事情是因为innerScope使用了TransactionScopeOption.Required,它加入了outerScope中使用的事务。一旦 innerScope 在 isCheck 为 false 时被释放,outerScope 也会被释放(这是我不想发生的事情!)所以当我在调用 MyMethod 后尝试获得另一个连接时,outerScope 已经被释放。

或者,如果我指定 TransactionOption.RequiresNew,我会得到这个异常:System.Data.SqlClient.SqlException : Timeout expired.

我已经尝试使用具有指定保存点的 SqlTransaction 和 TransactionOption 的不同组合都无济于事。

没有嵌套事务这样的东西。您可以嵌套范围,但嵌套范围所做的只是附加已经 运行 的事务。您不能独立于其他范围来处理内部范围(当然 RequiresNew 除外,它只是创建一个独立的事务)。

您想要的功能在 System.Transactions 中不存在。

保存点是创建类似嵌套事务的唯一方法。但是话又说回来 SQL 服务器很容易因为任意原因终止你的整个交易。什么错误回滚语句,什么错误回滚事务是不可预测的。 (是的,这没有意义。)