即使没有调用System.Transactions.TransactionScope.Commit(),数据仍然被提交了。

6
在什么情况下,即使抛出异常且最外层范围从未调用提交,仍然可以提交在System.Transactions.TransactionScope中包装的代码?
有一个顶层方法包装在using (var tx = new TransactionScope())中,并且该方法调用以相同方式使用TransactionScope的方法。
我正在使用带有关联表适配器的类型化数据集。可能是适配器中的命令由于某种原因没有注册?您是否知道如何检查它们是否在环境TransactionScope中注册?

有嵌套事务吗? - almog.ori
TransactionScope是可以嵌套的。有一个顶层方法被包装在using (var tx = new TransactionScope())中,它调用了使用TransactionScope的其他方法。 - Neil Barnwell
5个回答

10
答案原来是因为我在创建SqlConnection对象之后才创建TransactionScope对象。
我从这里搬移过来:
using (new ConnectionScope())
using (var transaction = new TransactionScope())
{
    // Do something that modifies data

    transaction.Complete();
}

变成这样:

using (var transaction = new TransactionScope())
using (new ConnectionScope())
{
    // Do something that modifies data

    transaction.Complete();
}

现在它可以工作了!

故事的寓意是首先创建你的TransactionScope


2
它也展示了发布代码的价值 ;-p 我们可能已经为您发现了它;不要紧 - 现在已经解决了。 - Marc Gravell
1
是的,但在这种情况下我做不到,因为应用程序太复杂了,而且我还没有在一个更小的示例中复制它。 - Neil Barnwell
4
我认为你可以先创建 SqlConnection(假设你需要将其传递给多个使用 TransactionScope 的方法)。如果你已经有了一个现有的连接且想要创建一个事务作用域,则可以在该作用域的 using 块内调用 connection.EnlistTransaction(Transaction.Current)。 - Triynko

2
显然的情况是当新的 (RequiresNew) / 空的 (Suppress) 事务被明确指定时 - 但也存在超时/解绑故障,可能导致连接错过事务。请参见此早期帖子(修复仅为连接字符串更改),或完整详情

我之前在研究问题时尝试更改连接字符串,但在这种情况下没有帮助。我想我可能在某个地方使用了“RequiresNew”,所以我会去看一下。 - Neil Barnwell
我正在使用带有关联表适配器的类型化数据集。可能是适配器中的命令由于某种原因未注册?您知道如何检查它们是否在环境TransactionScope中注册吗? - Neil Barnwell
最终我找到了答案,与创建TransactionScope和连接对象的顺序有关。我已经将我的答案添加到了这篇文章中。 - Neil Barnwell

0
请注意如何使用TransactionScope
它在使用范围的开始处设置属性System.Transactions.Transaction.Current,然后在使用范围结束时将其设置回先前的值。

先前的值取决于给定范围声明的位置。它可以在另一个范围内。


您可以像这样修改代码:

using (var sqlConnection = new ConnectionScope())
using (var transaction = new TransactionScope())
{
    sqlConnection.EnlistTransaction(System.Transactions.Transaction.Current);
    // Do something that modifies data
    transaction.Complete();
}

我展示这种可能性是为了那些代码更复杂,不能简单地改变代码以首先打开DB连接的人。


0

这个示例(C#,.NetFramework 4.7.1)展示了即使代码被包装在TransactionScope中,我们也可以将其持久化到数据库中。第一个insert将被回滚,第二个insert将在没有事务的情况下插入。

请参阅我的相关帖子,我在其中寻求如何检测此问题的帮助。


using (var transactionScope = new TransactionScope())
{
    using (var connection = new SqlConnection("Server=localhost;Database=TestDB;Trusted_Connection=True"))
    {
        connection.Open();

        new SqlCommand($"INSERT INTO TestTable VALUES('This will be rolled back later')", connection).ExecuteNonQuery();

        var someNestedTransaction = connection.BeginTransaction();
        someNestedTransaction.Rollback();

        new SqlCommand($"INSERT INTO TestTable VALUES('This is not in a transaction, and will be committed')", connection).ExecuteNonQuery();
    }

    throw new Exception("Exiting.");

    transactionScope.Complete();
}


-2
using (TransactionScope scope = new TransactionScope(TransactionScopeAsyncFlowOption.Enabled))
    {
        try
        {
            using (IDbConnection sqlConnection = GetDbconnection("SQL_CONNECTION_STRING"))
            {
                using (IDbConnection db2Connection = GetDbconnection("DB2_CONNECTION_STRING"))
                {
                    sqlConnection.Open();
                    db2Connection.Open();

                    string db2Query1 = "";  // INSERT QUERY
                    result = await db2Connection.QueryFirstOrDefaultAsync<bool>(db2Query1, new DynamicParameters(), commandType: CommandType.Text);
                    
                    string db2Query2 = "";  // INSERT QUERY
                    result = await db2Connection.QueryFirstOrDefaultAsync<bool>(db2Query2, new DynamicParameters(), commandType: CommandType.Text);

                    string procName = "SP_NAME";
                    DynamicParameters dynamicParams = new DynamicParameters();
                    dynamicParams.Add("Parameter1", "VALUE1", DbType.String);
                    GridReader sqlResult = await sqlConnection.QueryMultipleAsync(procName, dynamicParams, commandType: CommandType.StoredProcedure, commandTimeout: 30);
                    resultSet = await sqlResult.ReadAsync<T>();
                    int sqlExecResult = await sqlConnection.ExecuteAsync(procName, dynamicParams, commandTimeout: 30, commandType: CommandType.StoredProcedure);

                    scope.Complete();  // COMPLETE THE TRANSACTION
                    IsSuccess = true;
                }
            }
        }
        catch (Exception ex)
        {
            errorMessage = ex.Message;
            throw;
        }
    }

这个问题只在DB2上出现,我可以看到一旦插入就立即提交。

但是SQL Server的回滚工作正常。在调用complete()之前,SQL Server会等待提交,但DB2会立即提交。

请告诉我我的代码有什么问题


1
这并没有真正回答问题。如果你有其他问题,可以点击提问来提出。如果你想在这个问题有新的回答时收到通知,你可以关注此问题。一旦你拥有足够的声望,你还可以添加悬赏以吸引更多关注。- 来自审核 - ChrisGPT was on strike

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接