获取TransactionScope以使用异步/等待


114

我试图整合async/ await到我们的服务总线。我SingleThreadSynchronizationContext基于此示例http://blogs.msdn.com/b/pfxteam/archive/2012/01/20/10259049.aspx实现了一个。

它工作正常,除了一两件事:TransactionScope。我等待里面的东西TransactionScope,它打破了TransactionScope

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

“ TransactionScope嵌套不正确。”。

我试图TransactionScope在排队任务之前保存数据,并在运行它之前将其还原,但似乎并没有改变任何事情。和TransactionScope代码是一个烂摊子,所以真的很难理解发生了什么事情在那里。

有办法使它起作用吗?有TransactionScope什么替代方法吗?


这是一个非常简单的代码,可重现TransactionScope错误pastebin.com/Eh1dxG4a,但这里的异常是Transaction Aborted
Yann 2012年

您能否仅使用常规SQL事务?还是您正在跨多个资源?
马克·格雷夫

我涉及多个资源
Yann

看起来您需要将范围传递到异步方法中,或为它提供一种从工作单元所标识的某种通用上下文中检索范围的方法。
Bertrand Le Roy

SingleThreadSynchronizationContext每个顶层都需要一个单独的线程,每个线程都有自己的线程TransactionScope
Stephen Cleary 2012年

Answers:


161

在.NET Framework 4.5.1中,有一组新的构造函数TransactionScope需要使用TransactionScopeAsyncFlowOption参数函数。

根据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();
}

10

答案来得有点晚,但是我在MVC4中遇到了同样的问题,并且通过右键单击项目转到属性,将项目从4.5更新到4.5.1。选择“应用程序”选项卡,将目标框架更改为4.5.1并按以下方式使用事务。

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


6

您可以使用由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();
}

使用DependentTransaction管理并发

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


2
Adam Prescott的示例子任务未标记为异步。如果用类似await Task.Delay(500)这种模式的东西代替“做交易的东西”,也会失败,TransactionScope nested incorrectly因为最外面的TransactionScope(在上面的示例中未显示)在子任务正确完成之前就退出了作用域。替换awaitTask.Wait(),它可以工作,但随后您失去了的好处async
mdisibio 2014年

这是解决问题的更困难的方法。TransactionScope将隐藏所有管道。
Eniola
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.