Получите TransactionScope для работы с async/wait

Я пытаюсь интегрировать 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?

Ответ 1

В .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();
}

Ответ 2

Бит поздно для ответа, но у меня была такая же проблема с MVC4, и я обновил свой проект с 4.5 до 4.5.1, щелкнув правой кнопкой мыши по проекту, перейдите к свойствам. Выберите закладку приложения, чтобы изменить целевую структуру до 4.5.1 и используйте транзакцию следующим образом.

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

Ответ 3

Вы можете использовать DependentTransaction, созданный Transaction.DependentClone():

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

Управление Concurrency с DependentTransaction

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