展平处理的AggregateExceptions

我遇到了一些我在AggregateException上调用flatten问题,但是里面仍然存在另一个AggregateException ! 这显然意味着它们正在链中传播并被转换为另一个AggregateException 。 有没有办法递归展平所有内部AggregateExceptions? 通常,我将使用handle delegate来处理这些,但如果有另一个内部AggregateExceeption则返回false。 我没有正确处理这些问题吗?

编辑:因为我已经在调用Flatten,所以看起来问题是它直到稍后在callstack中被捕获。 这是我正在调用Flatten()的代码。 要在堆栈跟踪中使用,此方法称为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); 

但是,堆栈跟踪显示它没有被捕获。 相反,它会在调用堆栈之后被捕获。 以下是出于安全原因删除了一些识别信息的跟踪:

 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.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.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.<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.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.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.<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.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.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.<b__2>d__13.MoveNext() in c:\Users\XYZ\Development\PDI\PDI\ProcessPipeline.cs:line 61<---  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.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.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.<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.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.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.<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.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.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.<b__2>d__13.MoveNext() in c:\Users\XYZ\Development\PDI\PDI\ProcessPipeline.cs:line 61<--- 

顺便说一句:这是由TPL-Dataflow块调用的。

是的,这正是你所要求的:

 AggreggateException.Flatten() 

将经历并将所有内容压缩为单个AggregateException。 所以你可以使用它循环遍历所有内部exception,如下所示:

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

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

请记住,’flatten’方法会为您提供一个Exceptions列表,但仍然可以在每个Exception中留下扁平的InnerExceptions。

所以我发现这还不够:

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

因为这个例外:

System.Net.Http.HttpRequestException:发送请求时发生错误。 —> System.Net.WebException:无法连接到远程服务器—> System.Net.Sockets.SocketException:连接尝试失败,因为连接方在一段时间后没有正确响应,或建立连接失败,因为连接的主机无法在System.Net.Service.Service.ConnectSocketInternal的System.Net.Sockets.Socket.EndConnect(IAsyncResult asyncResult)上响应192.168.42.55:443(布尔connectFailure,套接字s4,套接字s6,套接字和套接字,IP地址和地址,ConnectSocketState状态,IAsyncResult asyncResult,exception和exception)—内部exception堆栈跟踪的结束—在System.Net.Http.HttpClientHandler.GetRequestStreamCallback(IAsyncResult ar)的System.Net.HttpWebRequest.EndGetRequestStream(IAsyncResult asyncResult,TransportContext&context)中)—内部exception堆栈跟踪结束—

最终会像这样:

发送请求时发生错误。

修复是这样的:

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

导致:

发送请求时发生错误。

无法连接到远程服务器

连接尝试失败,因为连接方在一段时间后没有正确响应,或者建立的连接失败,因为连接的主机无法响应192.168.42.54:443

希望能帮助别人。

这是一个老问题,但OP遇到的问题是await不会从等待的任务中公开AggregateException,而只是 AggregateException中的第一个exception 。 因此绕过了catch(AggregateException ex)块,并且exception被捕获到堆栈的更高位置。 所以代码应该是’简单’:

 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); 

或者, Jon Skeet的WithAllExceptions扩展方法允许通过将任务包装在另一个任务中来“保护”AggregateException,从而获得包含AggregateException的AggregateException,并且await’返回’原始/内部AggregateException。

注意: AggregateException.Flatten确实以递归方式“展平”,如MSDN页面上的示例所示。

编辑:改进重试延迟需要避免设置错误的异步示例。

通常,AggregateException用于将多个失败合并到一个可抛出的exception对象中。

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

尝试下面的示例代码,这应该避免await解包AggregateException并在调用task.result时抛出原始的AggregateException。

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