web-dev-qa-db-fra.com

Faites fonctionner TransactionScope avec async / wait

J'essaie d'intégrer async/await dans notre bus de service. J'ai implémenté un SingleThreadSynchronizationContext basé sur cet exemple http://blogs.msdn.com/b/pfxteam/archive/2012/01/20/10259049.aspx .

Et cela fonctionne très bien, sauf pour une chose: TransactionScope. J'attends des trucs à l'intérieur du TransactionScope et ça casse le TransactionScope.

TransactionScope ne semble pas jouer à Nice avec le async/await, certainement parce qu'il stocke les choses dans le thread en utilisant ThreadStaticAttribute. Je reçois cette exception:

"TransactionScope imbriqué incorrectement.".

J'ai essayé de sauvegarder les données de TransactionScope avant de mettre la tâche en file d'attente et de la restaurer avant de l'exécuter, mais cela ne semble rien changer. Et le code TransactionScope est un gâchis, il est donc très difficile de comprendre ce qui se passe là-bas.

Existe-t-il un moyen de le faire fonctionner? Existe-t-il une alternative à TransactionScope?

110
Yann

Dans .NET Framework 4.5.1, il existe un ensemble de nouveaux constructeurs pour TransactionScope qui prennent un paramètre TransactionScopeAsyncFlowOption.

Selon le MSDN, il permet le flux de transactions entre les suites de threads.

Ma compréhension est que cela est destiné à vous permettre d'écrire du code comme ceci:

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

Un peu tard pour une réponse, mais j'avais le même problème avec MVC4 et j'ai mis à jour mon projet de 4.5 à 4.5.1 en cliquant avec le bouton droit sur le projet, accédez aux propriétés. Sélectionnez l'onglet d'application pour changer le cadre cible en 4.5.1 et utilisez la transaction comme suit.

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

Vous pouvez utiliser la méthode DependentTransaction créée par la méthode 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();
}

Gestion de la concurrence avec DependentTransaction

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

6
maximpa