2014-04-04 16 views
11

Występują w kilku przypadkach, w których nazywam się flatten na AggregateException, ale w środku jest jeszcze INNY AggregateException! Oznacza to oczywiście, że są one propagowane w górę łańcucha i są przekształcane w kolejne AggregateException. Czy istnieje sposób rekurencyjnego spłaszczenia WSZYSTKICH wewnętrznych wyjątków AggregateExceptions? Zwykle użyję delegata uchwytów, aby je przetworzyć, ale zwraca on wartość false, jeśli istnieje inny wewnętrzny błąd AggregateException. Czy nie postępuję właściwie?Spłaszczanie wyjątków AggregateException do przetwarzania

EDYCJA: Ponieważ już wywołuję Flatten, wydaje się, że problem polega na tym, że nie zostanie on przechwycony do czasu później w obszarze wywołania. Oto kod, w którym dzwonię do Flatten(). Do stosowania w stosie prześledzić ta metoda nazywa WriteExceptionRecord (string, FileInfo):

do 
{ 
    try 
    { 
     using (var stream = file.Open(FileMode.Append, FileAccess.Write, FileShare.None)) 
     { 
      using (StreamWriter writer = new StreamWriter(stream)) 
      { 
       await writer.WriteLineAsync(data); 
      } 
     } 
    } 
    catch (AggregateException ex) 
    { 
     ex.Flatten().Handle((x) => 
     { 
      if (x is IOException) 
      { 
       retryNeeded = true; 
       retryLeft--; 
       Thread.Sleep(500); 
       return true; 
      } 

      logger.ErrorException("Could not write to exception file: " + data, ex); 
      return false; 
     }); 
    } 
} 
while (retryNeeded && retryLeft > 0); 

Jednak ślad stosu wskazuje, że nie jest złapany jest tutaj. Zamiast tego jest on przechwytywany później w górę stosu wywołań. Poniżej znajduje się ślad z pewnym pozbawionej informacji identyfikujących ze względów bezpieczeństwa:

System.AggregateException: One or more errors occurred. --->  
System.AggregateException: One or more errors occurred. ---> 
System.IO.IOException: The process cannot access the file 'X:\Production\ProductionBatches\DataEntry\J\PD\Exception.csv' because it is being used by another process.  
    at System.IO.__Error.WinIOError(Int32 errorCode, String maybeFullPath)  
    at System.IO.FileStream.Init(String path, FileMode mode, FileAccess access, Int32 rights, Boolean useRights, FileShare share, Int32 bufferSize, FileOptions options, SECURITY_ATTRIBUTES secAttrs, String msgPath, Boolean bFromProxy, Boolean useLongPath, Boolean checkHost)  
    at System.IO.FileStream..ctor(String path, FileMode mode, FileAccess access, FileShare share)  
    at System.IO.FileInfo.Open(FileMode mode, FileAccess access, FileShare share)  
    at PDI.LoadFileProcessing.<WriteExceptionRecord>d__21.MoveNext() in c:\Users\XYZ\Development\PDI\PDI\LoadFileProcessing.cs:line 328 

--- End of stack trace from previous location where exception was thrown --- 
    at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task) 
    at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) 
    at System.Runtime.CompilerServices.TaskAwaiter.GetResult() 
    at PDI.LoadFileProcessing.<ExceptionRecordProcessing>d__17.MoveNext() in c:\Users\XYZ\Development\PDI\PDI\LoadFileProcessing.cs:line 316 

--- End of stack trace from previous location where exception was thrown --- 
    at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task) 
    at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) 
    at System.Runtime.CompilerServices.TaskAwaiter.GetResult() 
    at PDI.ProcessPipeline.<>c__DisplayClass9.<<ProcessBatch>b__2>d__13.MoveNext() in c:\Users\XYZ\Development\PDI\PDI\ProcessPipeline.cs:line 61 

    --- End of inner exception stack trace --- 

    --- End of inner exception stack trace --- 

---> (Inner Exception #0) System.AggregateException: One or more errors occurred. ---> System.IO.IOException: The process cannot access the file 'X:\Production\ProductionBatches\DataEntry\J\PD\Exception.csv' because it is being used by another process. 
    at System.IO.__Error.WinIOError(Int32 errorCode, String maybeFullPath) 
    at System.IO.FileStream.Init(String path, FileMode mode, FileAccess access, Int32 rights, Boolean useRights, FileShare share, Int32 bufferSize, FileOptions options, SECURITY_ATTRIBUTES secAttrs, String msgPath, Boolean bFromProxy, Boolean useLongPath, Boolean checkHost) 
    at System.IO.FileStream..ctor(String path, FileMode mode, FileAccess access, FileShare share) 
    at System.IO.FileInfo.Open(FileMode mode, FileAccess access, FileShare share) 
    at PeopleDocImporter.LoadFileProcessing.<WriteExceptionRecord>d__21.MoveNext() in c:\Users\XYZ\Development\PDI\PDI\LoadFileProcessing.cs:line 328 

--- End of stack trace from previous location where exception was thrown --- 
    at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task) 
    at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) 
    at System.Runtime.CompilerServices.TaskAwaiter.GetResult() 
    at PDI.LoadFileProcessing.<ExceptionRecordProcessing>d__17.MoveNext() in c:\Users\XYZ\Development\PDI\PDI\LoadFileProcessing.cs:line 316 

--- End of stack trace from previous location where exception was thrown --- 
    at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task) 
    at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) 
    at System.Runtime.CompilerServices.TaskAwaiter.GetResult() 
    at PDI.ProcessPipeline.<>c__DisplayClass9.<<ProcessBatch>b__2>d__13.MoveNext() in c:\Users\XYZ\Development\PDI\PDI\ProcessPipeline.cs:line 61 

    --- End of inner exception stack trace --- 
---> (Inner Exception #0) System.IO.IOException: The process cannot access the file 'X:\Production\ProductionBatches\DataEntry\J\PD\Exception.csv' because it is being used by another process. 
    at System.IO.__Error.WinIOError(Int32 errorCode, String maybeFullPath) 
    at System.IO.FileStream.Init(String path, FileMode mode, FileAccess access, Int32 rights, Boolean useRights, FileShare share, Int32 bufferSize, FileOptions options, SECURITY_ATTRIBUTES secAttrs, String msgPath, Boolean bFromProxy, Boolean useLongPath, Boolean checkHost) 
    at System.IO.FileStream..ctor(String path, FileMode mode, FileAccess access, FileShare share) 
    at System.IO.FileInfo.Open(FileMode mode, FileAccess access, FileShare share) 
    at PDI.LoadFileProcessing.<WriteExceptionRecord>d__21.MoveNext() in c:\Users\XYZ\Development\PDI\PDI\LoadFileProcessing.cs:line 328 

--- End of stack trace from previous location where exception was thrown --- 
    at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task) 
    at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) 
    at System.Runtime.CompilerServices.TaskAwaiter.GetResult() 
    at PDI.LoadFileProcessing.<ExceptionRecordProcessing>d__17.MoveNext() in c:\Users\XYZ\Development\PDI\PDI\LoadFileProcessing.cs:line 316 

--- End of stack trace from previous location where exception was thrown --- 
    at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task) 
    at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)  
    at System.Runtime.CompilerServices.TaskAwaiter.GetResult()  
    at PDI.ProcessPipeline.<>c__DisplayClass9.<<ProcessBatch>b__2>d__13.MoveNext() in c:\Users\XYZ\Development\PDI\PDI\ProcessPipeline.cs:line 61<--- 

<--- 

System.AggregateException: One or more errors occurred. ---> System.AggregateException: One or more errors occurred. ---> System.IO.IOException: The process cannot access the file 'X:\Production\ProductionBatches\DataEntry\J\PD\Exception.csv' because it is being used by another process.  
    at System.IO.__Error.WinIOError(Int32 errorCode, String maybeFullPath)  
    at System.IO.FileStream.Init(String path, FileMode mode, FileAccess access, Int32 rights, Boolean useRights, FileShare share, Int32 bufferSize, FileOptions options, SECURITY_ATTRIBUTES secAttrs, String msgPath, Boolean bFromProxy, Boolean useLongPath, Boolean checkHost)  
    at System.IO.FileStream..ctor(String path, FileMode mode, FileAccess access, FileShare share)  
    at System.IO.FileInfo.Open(FileMode mode, FileAccess access, FileShare share)  
    at PDI.LoadFileProcessing.<WriteExceptionRecord>d__21.MoveNext() in c:\Users\XYZ\Development\PDI\PDI\LoadFileProcessing.cs:line 328 

--- End of stack trace from previous location where exception was thrown ---  
    at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)  
    at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)  
    at System.Runtime.CompilerServices.TaskAwaiter.GetResult()  
    at PDI.LoadFileProcessing.<ExceptionRecordProcessing>d__17.MoveNext() in c:\Users\XYZ\Development\PDI\PDI\LoadFileProcessing.cs:line 316 

--- End of stack trace from previous location where exception was thrown ---  
    at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)  
    at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)  
    at System.Runtime.CompilerServices.TaskAwaiter.GetResult()  
    at PDI.ProcessPipeline.<>c__DisplayClass9.<<ProcessBatch>b__2>d__13.MoveNext() in c:\Users\XYZ\Development\PDI\PDI\ProcessPipeline.cs:line 61 

    --- End of inner exception stack trace --- 

---> (Inner Exception #0) System.AggregateException: One or more errors occurred. ---> System.IO.IOException: The process cannot access the file 'X:\J\PD\Exception.csv' because it is being used by another process.  
    at System.IO.__Error.WinIOError(Int32 errorCode, String maybeFullPath)  
    at System.IO.FileStream.Init(String path, FileMode mode, FileAccess access, Int32 rights, Boolean useRights, FileShare share, Int32 bufferSize, FileOptions options, SECURITY_ATTRIBUTES secAttrs, String msgPath, Boolean bFromProxy, Boolean useLongPath, Boolean checkHost)  
    at System.IO.FileStream..ctor(String path, FileMode mode, FileAccess access, FileShare share)  
    at System.IO.FileInfo.Open(FileMode mode, FileAccess access, FileShare share)  
    at PDI.LoadFileProcessing.<WriteExceptionRecord>d__21.MoveNext() in c:\Users\XYZ\Development\PDI\PDI\LoadFileProcessing.cs:line 328 

--- End of stack trace from previous location where exception was thrown ---  
    at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)  
    at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)  
    at System.Runtime.CompilerServices.TaskAwaiter.GetResult()  
    at PDI.LoadFileProcessing.<ExceptionRecordProcessing>d__17.MoveNext() in c:\Users\XYZ\Development\PDI\PDI\LoadFileProcessing.cs:line 316 

--- End of stack trace from previous location where exception was thrown ---  
    at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)  
    at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)  
    at System.Runtime.CompilerServices.TaskAwaiter.GetResult()  
    at PDI.ProcessPipeline.<>c__DisplayClass9.<<ProcessBatch>b__2>d__13.MoveNext() in c:\Users\XYZ\Development\PDI\PDI\ProcessPipeline.cs:line 61 

    --- End of inner exception stack trace ---  
---> (Inner Exception #0) System.IO.IOException: The process cannot access the file 'X:\Production\ProductionBatches\DataEntry\J\PD\Exception.csv' because it is being used by another process.  
    at System.IO.__Error.WinIOError(Int32 errorCode, String maybeFullPath)  
    at System.IO.FileStream.Init(String path, FileMode mode, FileAccess access, Int32 rights, Boolean useRights, FileShare share, Int32 bufferSize, FileOptions options, SECURITY_ATTRIBUTES secAttrs, String msgPath, Boolean bFromProxy, Boolean useLongPath, Boolean checkHost)  
    at System.IO.FileStream..ctor(String path, FileMode mode, FileAccess access, FileShare share)  
    at System.IO.FileInfo.Open(FileMode mode, FileAccess access, FileShare share)  
    at PDI.LoadFileProcessing.<WriteExceptionRecord>d__21.MoveNext() in c:\Users\XYZ\Development\PDI\PDI\LoadFileProcessing.cs:line 328 

--- End of stack trace from previous location where exception was thrown ---  
    at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)  
    at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)  
    at System.Runtime.CompilerServices.TaskAwaiter.GetResult()  
    at PDI.LoadFileProcessing.<ExceptionRecordProcessing>d__17.MoveNext() in c:\Users\XYZ\Development\PDI\PDI\LoadFileProcessing.cs:line 316 

--- End of stack trace from previous location where exception was thrown ---  
    at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)  
    at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)  
    at System.Runtime.CompilerServices.TaskAwaiter.GetResult()  
    at PDI.ProcessPipeline.<>c__DisplayClass9.<<ProcessBatch>b__2>d__13.MoveNext() in c:\Users\XYZ\Development\PDI\PDI\ProcessPipeline.cs:line 61<--- 

Nawiasem mówiąc: To jest nazywany przez TPL-dataflow bloków.

+0

To tylko standardowy uchwyt, który zwraca wartość true, jeśli jest to wyjątek IOException, a w przeciwnym razie wartość false. Zastanawiam się nad napisaniem 'Func ' rekursywnie, ale nie jestem pewien czy to jest prawidłowe zachowanie. – JNYRanger

+0

czy jesteś pewien, że wszystkie wyjątki są zgodne z IOException? –

+0

Tak. Mam je wyświetlane w Log2Console, ale wciąż są wyrzucane, gdy próbuję je złapać. – JNYRanger

Odpowiedz

17

Tak, jest dokładnie to, co prosisz:

AggreggateException.Flatten() 

przejdzie i kompresować wszystko w dół do jednego AggregateException. Więc można go używać do pętli przez wszystkich wewnętrznych wyjątkami takimi jak ten:

try 
{ 
    // something dangerous 
} 
catch (AggregateException ae) 
{ 
    foreach(var innerException in ae.Flatten().InnerExceptions) 
    { 
     // handle error 
    } 
} 

MSDN link: http://msdn.microsoft.com/en-us/library/system.aggregateexception.flatten.aspx

+0

Hm ... może robię coś nie tak, ponieważ to właśnie robię. – JNYRanger

+0

@JNYRanger Dodano trochę więcej przykładowego kodu. Jeśli to nie pomoże, możesz opisać, jak wygląda drzewo wyjątków, a wyniki '.Flatten()' podają ci więcej szczegółów? –

+0

Opublikuję kilka przykładowych kodów jutro, jednak jutro zrobię trochę testów. – JNYRanger

4

Normalnie AggregateException służy do konsolidacji wielu niepowodzeń w jednym, Throwable obiektu wyjątku.

try { 
      Task.WaitAll(tasks) 
     } 
     catch (AggregateException ae) { 
      ae.Handle((x) => 
      { 
       if (x is UnauthorizedAccessException) // This we know how to handle. 
       { 
       //do your code here 
       } 
       return true; //if you do something like this all exceptions are marked as handled 
      }); 
     } 
6

To stara sprawa, ale problem doświadczane przez PO jest to, że czekają nie narażać AggregateException od oczekiwanego zadania, lecz tylko pierwszy wyjątek w AggregateException. Tak więc blok catch (AggregateException ex) jest pomijany, a wyjątek jest przechwytywany dalej w górę stosu. Więc kod powinien być „po prostu”:

retryNeeded = false; 
do 
{ 
    try 
    { 
     if (retryNeeded) 
      await Task.Delay(500); // substituted for Thread.Sleep 

     using (var stream = file.Open(FileMode.Append, FileAccess.Write, FileShare.None)) 
     { 
      using (StreamWriter writer = new StreamWriter(stream)) 
      { 
       await writer.WriteLineAsync(data); 
       retryNeeded = false; 
      } 
     } 
    } 
    catch (IOException) 
    { 
     retryNeeded = true; 
     retryLeft--; 
    } 
    catch (Exception ex) 
    { 
     logger.ErrorException("Could not write to exception file: " + data, ex); 
     throw; 
    } 
} while (retryNeeded && retryLeft > 0); 

return (retryLeft > 0); 

Alternatywnie Jon Skeet's WithAllExceptions extension method pozwala na „ochronę” z AggregateException z zachowaniem czekają poprzez owijanie zadanie w innym zadaniu więc masz AggregateException zawierający AggregateException i czekają na „powraca "oryginalny/wewnętrzny wyjątek AggregateException.

UWAGA: AggregateException.Flatten naprawdę oznacza „spłaszczyć” rekurencyjnie, jak pokazano na przykładzie na the MSDN page.

EDYTOWANIE: Poprawione opóźnienie przy ponownym uruchomieniu, aby uniknąć ustawienia nieprawidłowego przykładu asynchronicznego.

4

Należy pamiętać, że metoda "spłaszczania" zawiera listę wyjątków, ale nadal może pozostawiać spłaszczone wewnętrzne wyjątki w każdym wyjątku.

Więc stwierdziliśmy, że to nie było wystarczające:

try 
{ 
    // something dangerous 
} 
catch (AggregateException ae) 
{ 
    foreach(Exception innerException in ae.Flatten().InnerExceptions) 
    { 
     Console.WriteLine(innerException.Message()); 
    } 
} 

Ponieważ ten wyjątek:

System.Net.Http.HttpRequestException: Wystąpił błąd podczas wysyłania żądania. ---> System.Net.WebException: Nie można połączyć się ze zdalnym serwerem ---> System.Net.Sockets.SocketException: próba połączenia nie powiodła się, ponieważ strona nawiązana połączenie nie odpowiedziała poprawnie po pewnym czasie lub ustanowione połączenie nie powiodło się, ponieważ podłączony host nie odpowiedział 192.168.42.55:443 na System.Net.Sockets.Socket.EndConnect (IAsyncResult asyncResult) w System.Net.ServicePoint.ConnectSocketInternal (Boolean connectFailure, Socket s4, Socket s6, Socket & gniazdo , adres_IP & adres stan ConnectSocketState, IAsyncResult asyncResult, wyjątek & wyjątkiem) --- Koniec wewnętrznej śledzenia wyjątku stosu --- w System.Net.HttpWebRequest.EndGetRequestStream (IAsyncResult asyncResult, TransportContext & kontekstu) w System.Net.Http.HttpClientHandler.GetRequestStreamCallback (IAsyncResult Ar) --- koniec wewnętrznej śledzenia wyjątku stosu ---

zakończy się jak poniżej:

błąd wystąpił podczas wysłanie żądania.

Rozwiązaniem było coś takiego:

foreach(Exception exInnerException in aggEx.Flatten().InnerExceptions) 
{ 
    Exception exNestedInnerException = exInnerException; 
    do 
    { 
     if (!string.IsNullOrEmpty(exNestedInnerException.Message)) 
     { 
      Console.WriteLine(exNestedInnerException.Message); 
     } 
     exNestedInnerException = exNestedInnerException.InnerException; 
    } 
    while (exNestedInnerException != null); 
} 

skutkuje:

Wystąpił błąd podczas wysyłania żądania.

Nie można połączyć się ze zdalnym serwerem

Próba połączenia nie powiodła się, ponieważ połączona strona nie odpowiedziała poprawnie po pewnym czasie, lub ustanowione połączenie nie powiodło się, ponieważ połączony host nie odpowiedział 192.168.42.54:443

Mam nadzieję, że komuś pomóż.

+0

To powinna być zaakceptowana odpowiedź. – Martin

0

Wypróbuj przykładowy kod poniżej, powinno to zapobiec await rozpakowaniu AggregateException i rzucić oryginalny wyjątek AggregateException w miejscu, w którym wywoływana jest funkcja taskres.

var task = writer.WriteLineAsync(data); 
      await task.ContinueWith(t => { }, TaskContinuationOptions.ExecuteSynchronously)); 
      return task.Result; 
Powiązane problemy