Uzyskaj TransactionScope do pracy z async / await


114

Próbuję zintegrować async/ awaitz naszą magistralą usług. Zaimplementowałem na SingleThreadSynchronizationContextpodstawie tego przykładu http://blogs.msdn.com/b/pfxteam/archive/2012/01/20/10259049.aspx .

I to działa dobrze, z wyjątkiem jednej rzeczy: TransactionScope. Czekam na rzeczy w środku TransactionScopei to zepsuje TransactionScope.

TransactionScopenie wydaje się działać dobrze z async/ await, z pewnością dlatego, że przechowuje rzeczy w wątku przy użyciu ThreadStaticAttribute. Mam ten wyjątek:

„TransactionScope zagnieżdżony nieprawidłowo.”.

Próbowałem zapisać TransactionScopedane przed umieszczeniem zadania w kolejce i przywrócić je przed uruchomieniem, ale wydaje się, że nic to nie zmienia. A TransactionScopekod to bałagan, więc naprawdę trudno jest zrozumieć, co się tam dzieje.

Czy jest sposób, aby to zadziałało? Czy jest jakaś alternatywa TransactionScope?


Oto bardzo prosty kod do odtworzenia błędu TransactionScope pastebin.com/Eh1dxG4a, z tym wyjątkiem, że wyjątkiem jest tutaj transakcja przerwana
Yann

Czy możesz po prostu użyć zwykłej transakcji SQL? A może korzystasz z wielu zasobów?
Marc Gravell

Obejmuję wiele zasobów
Yann

Wygląda na to, że musisz albo przekazać zakres do metody asynchronicznej, albo dać mu sposób na pobranie go z jakiegoś wspólnego kontekstu, który jest identyfikowany z twoją jednostką roboczą.
Bertrand Le Roy

Będziesz potrzebować osobnego wątku z własnym SingleThreadSynchronizationContextdla każdego najwyższego poziomu TransactionScope.
Stephen Cleary,

Odpowiedzi:


161

W .NET Framework 4.5.1 istnieje zestaw nowych konstruktorów,TransactionScope które pobierają TransactionScopeAsyncFlowOptionparametr.

Według MSDN umożliwia przepływ transakcji przez kontynuacje wątków.

Rozumiem, że ma to umożliwić pisanie takiego kodu:

// 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

Trochę za późno na odpowiedź, ale miałem ten sam problem z MVC4 i zaktualizowałem mój projekt z 4.5 do 4.5.1, klikając prawym przyciskiem myszy projekt przejdź do właściwości. Wybierz kartę aplikacji, zmień platformę docelową na 4.5.1 i użyj transakcji w następujący sposób.

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

2
Czym różni się to od zaakceptowanej odpowiedzi?
Liam

6

Możesz użyć DependentTransaction utworzonej przez metodę 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();
}

Zarządzanie współbieżnością za pomocą DependentTransaction

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


2
Przykładowe zadanie podrzędne Adama Prescotta nie zostało oznaczone jako asynchroniczne. Jeśli zastąpisz „wykonaj czynności transakcyjne” czymś takim, jak await Task.Delay(500)ten wzorzec, również zakończy się niepowodzeniem, TransactionScope nested incorrectlyponieważ najbardziej zewnętrzny TransactionScope (nie pokazany w powyższym przykładzie) opuszcza zakres, zanim zadanie podrzędne zostanie poprawnie zakończone. Wymień awaitsię Task.Wait()i działa, ale potem straciłem korzyści async.
mdisibio

To trudniejszy sposób rozwiązania problemu. TransactionScope ma ukryć całą tę hydraulikę.
Eniola
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.