Дозвольте TransactionScope працювати з async / wait


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 за винятком того, що виключення тут угода Перерваний
Yann

Чи можете ви ніт просто використовувати звичайну транзакцію SQL? Або ви охоплюєте кілька ресурсів?
Марк Гравелл

Я охоплює кілька RESSOURCES
Yann

Схоже, вам потрібно буде або передати область застосування у ваш метод асинхронізації, або дати йому спосіб отримати його з якогось загального контексту, який ототожнюється з вашим робочим підрозділом.
Бертран Ле Рой

Вам знадобиться окрема нитка з власною SingleThreadSynchronizationContextдля кожного верхнього рівня TransactionScope.
Стівен Клірі

Відповіді:


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))
{
}

2
Чим це відрізняється від прийнятої відповіді?
Ліам

6

Ви можете використовувати 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();
}

Управління паралельністю з DependentTransaction

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


2
Приклад дитячого завдання Адама Прескотта не було позначено асинхронністю. Якщо ви заміните "робити транзакційні речі" чимось на зразок await Task.Delay(500)цього шаблону, також не вдасться, TransactionScope nested incorrectlyоскільки найкоротший TransactionScope (не показаний у наведеному вище прикладі) виходить із сфери застосування, перш ніж дочірнє завдання належним чином виконане. Замінити awaitз , Task.Wait()і вона працює, але ви втратили перевагу async.
mdisibio

Це важчий спосіб вирішити проблему. TransactionScope - це приховати всю сантехніку.
Еніола
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.