ChatGPT解决这个技术问题 Extra ChatGPT

获取 TransactionScope 以使用 async / await

我正在尝试将 async/await 集成到我们的服务总线中。我基于此示例 http://blogs.msdn.com/b/pfxteam/archive/2012/01/20/10259049.aspx 实现了一个 SingleThreadSynchronizationContext

它工作正常,除了一件事:TransactionScope。我等待 TransactionScope 中的内容,它破坏了 TransactionScope

TransactionScope 似乎不能与 async/await 配合使用,这肯定是因为它使用 ThreadStaticAttribute 将内容存储在线程中。我得到这个例外:

“TransactionScope 嵌套不正确。”。

我试图在排队任务之前保存 TransactionScope 数据并在运行它之前恢复它,但它似乎并没有改变任何事情。而且 TransactionScope 代码是一团糟,所以很难理解那里发生了什么。

有没有办法让它工作? TransactionScope 有什么替代品吗?

这是重现 TransactionScope 错误 pastebin.com/Eh1dxG4a 的非常简单的代码,只是这里的异常是 Transaction Aborted
您可以只使用常规的 SQL 事务吗?还是您跨越多个资源?
我跨越多个资源
看起来您需要将范围传递给您的异步方法,或者给它一种从您的工作单元标识的某种常见上下文中检索它的方法。
对于每个顶级 TransactionScope,您需要一个具有自己的 SingleThreadSynchronizationContext 的单独线程。

L
Liam

在 .NET Framework 4.5.1 中,有一组采用 TransactionScopeAsyncFlowOption 参数的 new constructors for TransactionScope

根据 MSDN,它支持跨线程延续的事务流。

我的理解是,它是为了让您可以编写如下代码:

// transaction scope
using (var scope = new TransactionScope(... ,
  TransactionScopeAsyncFlowOption.Enabled))
{
  // connection
  using (var connection = new SqlConnection(_connectionString))
  {
    // open connection asynchronously
    await connection.OpenAsync();

    using (var command = connection.CreateCommand())
    {
      command.CommandText = ...;

      // run command asynchronously
      using (var dataReader = await command.ExecuteReaderAsync())
      {
        while (dataReader.Read())
        {
          ...
        }
      }
    }
  }
  scope.Complete();
}

a
abatishchev

回答有点晚了,但我在 MVC4 上遇到了同样的问题,我通过右键单击项目转到属性将我的项目从 4.5 更新到 4.5.1。选择应用程序选项卡将目标框架更改为 4.5.1 并使用事务,如下所示。

using (AccountServiceClient client = new AccountServiceClient())
using (TransactionScope scope = new TransactionScope(TransactionScopeAsyncFlowOption.Enabled))
{
}

这与 accepted answer? 有何不同
m
maximpa

您可以使用由 Transaction.DependentClone() 方法创建的 DependentTransaction

static void Main(string[] args)
{
  // ...

  for (int i = 0; i < 10; i++)
  {

    var dtx = Transaction.Current.DependentClone(
        DependentCloneOption.BlockCommitUntilComplete);

    tasks[i] = TestStuff(dtx);
  }

  //...
}


static async Task TestStuff(DependentTransaction dtx)
{
    using (var ts = new TransactionScope(dtx))
    {
        // do transactional stuff

        ts.Complete();
    }
    dtx.Complete();
}

Managing Concurrency with DependentTransaction

http://adamprescott.net/2012/10/04/transactionscope-in-multi-threaded-applications/


Adam Prescott 的示例子任务未标记为异步。如果您将“做事务处理”替换为 await Task.Delay(500) 之类的内容,则此模式也会因 TransactionScope nested incorrectly 而失败,因为最外面的 TransactionScope(上例中未显示)在子任务正确完成之前退出范围。将 await 替换为 Task.Wait() 即可,但您失去了 async 的好处。
这是解决问题的更难的方法。 TransactionScope 是为了隐藏所有的管道。
D
Don Reynolds

如果有人仍然关注这个线程......

Microsoft.Data.SqlClient v3.0.1 看起来修复了 System.Transactions 在 .NET Framework 的 async/await 函数中的死锁问题!我用的是4.8。我使用显式分布式事务方法允许通过使用多个连接对单个 sql 服务器执行并行查询,但我确信 TransactionScope 的行为也更好。

创建一个 CommittableTransaction,创建任意数量的 SqlConnections,这些 SqlConnections 包含从父可提交表创建的 DependentTransaction,并行执行连接上的查询,在查询执行后完成依赖项,然后提交/回滚父可提交表。

我在 3 个不同的连接上使用 3 个并行异步插入到同一个数据库中对其进行了测试。当父可提交正在进行时,我使用了一个消息框来提示提交或回滚。当分布式事务处于活动状态时,表被锁定。我无法在 ssms 中选择它们。选择提交或回滚后,要么按预期工作。

上周使用 Microsoft.Data.SqlClient 3.0.0,这是不可能的,因为 .Commit() 方法会在异步函数中死锁。我什至用各种方法尝试过 BeginCommit/EndCommit。甚至在异步函数中招募依赖项时也会出现问题,但这也已修复。现在最简单的显式分布式事务方法适用于 async/await。


我可能说得太早了关于招募家属的问题。今天经过大量测试,SqlConnection .EnlistTransaction() 似乎随机导致异常。一旦遇到异常,您将看到有关数据读取器正在打开并需要关闭的消息(我根本没有在这些测试中使用它们)或者因为事务在每次连续运行时都处于错误状态而无法登记。有时等待几分钟可以成功尝试,有时我必须重置 DTC 服务才能再次工作。至少没有死锁。
我的怀疑是,当您异步打开并登记一堆sql连接时,登记有时会失败,因为dtc无法正确提升事务或在一个或多个开始执行sql代码后无法将新连接添加到事务中.如果我在每次调用 EnlistTransaction() 后人为暂停 2 秒,让所有同时创建的连接有机会在执行查询之前登记,那么相同的代码将起作用。