6

Mam metodę asynchroniczną zwracającą zadanie.Wzorzec do implementacji metod synchronizacji w kontekście zadania nierównoległego (tłumaczenie/rozpakowywanie wyjątków AggregateException)

Chciałbym również zaoferować synchroniczny odpowiednik, ale nie chcę, aby konsumenci z niego musieli rozpakowywać się AggregateException s.

Teraz rozumiem, że cała idea polega na tym, że nie można arbitralnie wybrać jednego w sposób ogólny, i wiem, że mógłbym przeczytać więcej ładunków artykułów Stephena Touba (będę, ale nie teraz) i zrozumiem to wszystko i może sam decydować.

W międzyczasie chcę użyć faktu, że moje zadania są w rzeczywistości przykręcone "workflow" bez równoległości, po prostu interweniując Waits (nie, nie TPL DataFlow), które nie powinny skutkować więcej niż jednym wyjątkiem. W takim przypadku należałoby obsługiwać w następujący sposób:

CallAsync().Wait(); 
} 
catch(AggregateException ae) 
{ 
    throw ae.Flatten().First() 

czy mam zagwarantowane, że AggregateException zawsze ma InnerException nawet jeśli jest ich więcej niż jeden. A może jest przypadek, w którym powinienem wrócić do .Flatten().First()?


W niektórych OC docs, widzę odniesienie do metody Unwrap() na AggregateException (nie wiem, czy to rozszerzenie lub coś w wersji beta).

Jako zastępczy, robię:

void Call() 
{ 
    try 
    { 
     CallAsync().Wait(); 
    } 
    catch (AggregateException ex) 
    { 
     var translated = ex.InnerException ?? ex.Flatten().InnerExceptions.First(); 
     if (translated == null) 
      throw; 
     throw translated;     } 
} 

Task CallAsync(){ ... 

Odpowiedz

21

Nie ma „czysty” sposób na to, że znam zrobić. Nie można użyć wartości throw someInnerException;, ponieważ utracisz stos, gdy wyjątek pochodzi z przepływu pracy asynchronicznej, a jeśli po prostu użyjesz throw;, prawdopodobnie będziesz propagować kod AggregateException. To, co musiałbyś zrobić dla metody synchronicznej, ma pewien rodzaj wyjątku "opakowania", który można wypchnąć do pierwszego wyjątku od AggregateException, a następnie wyrzucić to konsekwentnie z synchronicznej wersji metody.

void Call() 
{ 
    try 
    { 
     CallAsync().Wait(); 
    } 
    catch (AggregateException ex) 
    { 
     throw new MyConsistentWrapperException("An exception occurred while executing my workflow. Check the inner exception for more details.", ex.Flatten().InnerExceptions.First()); 
    } 
} 

FWIW, że już rozwiązany to w 4.5 z the new ExceptionDispatchInfo class które pomogą Ci zebrać wyjątki całej wątków bez olbrzymi stos. Następnie można napisać synchroniczną wersję w następujący sposób:

void Call() 
{ 
    try 
    { 
     CallAsync().Wait(); 
    } 
    catch (AggregateException ex) 
    { 
     ExceptionDispatchInfo.Capture(ex.Flatten().InnerExceptions.First()).Throw(); 
    } 
} 
+1

Dzięki; specjalnie dla punktu straty stosu, który rozważałem, a następnie przymykałem oko (nie wiem nawet, czy normalna sztuczka PreserveStackTrace zadziała, ponieważ jestem pod średnim zaufaniem, więc nie mogę nawet próbować). Ostatnie pytanie (i mój prawdziwy punkt wyjścia) - jakikolwiek pomysł, dlaczego większość próbek używa '.InnerException' zamiast' .Flatten(). InnerExceptions.First() 'lub inny sposób rozpakowania - ** są one równoważne lub nie **? Jeśli tak, to wszelkie odniesienia? Czy istnieją inne sposoby rozpakowywania - wszelkie linki są doceniane ... –

+0

Jeśli spojrzysz na konstruktory AggregateException z ILDasmhem, zobaczysz, że ostatecznie istnieje prywatny konstruktor (string, IList ), który jest delegowany do innych implementacji i w tym konstruktorze wywołują konstruktor bazowy Exception przekazujący pierwszy wyjątek w IList jako parametr innerException. Jednakże, ponieważ pierwszy wyjątek może technicznie być kolejnym wyjątkiem AggregateException, użycie Flatten() jest gwarantowanym sposobem uzyskania pierwszego wyjątku leżącego u podstaw nieagregacyjnego wyjątku. –

+0

Fajnie, to doskonale wyjaśniłem. Chciałbym, aby jedna z 50 stron dokumentów i artykułów mogła wyjaśnić to jako udaną. (Nie mam jeszcze na TPL z dekompilatorem - z jakiegoś powodu uważam to za umysłowo poza granicami.Co do tego jak wyjaśniam nie przyjmując takiego stosunku do WCF ...: D) –