133 votes

Obtenir TransactionScope pour travailler avec async / await

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

Et ça marche bien, sauf pour une chose : TransactionScope . J'attends des trucs à l'intérieur du TransactionScope et il casse le TransactionScope .

TransactionScope n'a pas l'air de bien jouer avec le async / await certainement parce qu'il stocke les choses dans le fil en utilisant ThreadStaticAttribute . Je reçois cette exception :

"TransactionScope nested incorrect.".

J'ai essayé de sauver TransactionScope avant de mettre la tâche en file d'attente et de les restaurer avant de l'exécuter, mais cela ne semble rien changer. Et TransactionScope Le code est désordonné, il est donc très difficile de comprendre ce qui se passe.

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

0 votes

Voici un code très simple pour reproduire une erreur de TransactionScope pastebin.com/Eh1dxG4a sauf que l'exception ici est la Transaction Abortée

0 votes

Pouvez-vous simplement utiliser une transaction SQL ordinaire ? Ou bien vous utilisez plusieurs ressources ?

0 votes

Je couvre de multiples ressources

190voto

ZunTzu Points 783

Dans .NET Framework 4.5.1, il y a un ensemble d'outils d'aide à la décision. de nouveaux constructeurs pour TransactionScope qui prennent un TransactionScopeAsyncFlowOption paramètre.

Selon le MSDN, il permet le flux de transactions à travers les continuations de threads.

Si j'ai bien compris, il est censé vous permettre d'écrire du code comme celui-ci :

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

15voto

Atul Chaudhary Points 179

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 faisant un clic droit sur le projet et en allant dans les propriétés. Sélectionnez l'onglet application, changez 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))
{
}

6 votes

En quoi cela diffère-t-il de la réponse acceptée ?

6voto

maximpa Points 1119

Vous pouvez utiliser DependentTransaction créé par Transaction.DependentClone() méthode :

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 concurence avec DependentTransaction

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

2 votes

L'exemple de tâche enfant d'Adam Prescott n'était pas marqué asynchrone. Si vous remplacez "faire des trucs transactionnels" par quelque chose comme await Task.Delay(500) ce modèle échouera également avec TransactionScope nested incorrectly car la TransactionScope la plus extérieure (non représentée dans l'exemple ci-dessus) sort du champ d'application avant que la tâche enfant ne se termine correctement. Remplacer await con Task.Wait() et ça marche, mais alors vous avez perdu les avantages de async .

0 votes

C'est une façon plus difficile de résoudre le problème. TransactionScope permet de cacher toute cette plomberie.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X