聚合的平展处理异常

本文关键字:处理 异常 | 更新日期: 2023-09-27 17:56:37

我遇到了一些问题,我在AggregateException上打电话给flatten,但里面还有另一个AggregateException! 这显然意味着它们正在沿着链条向上传播并被卷入另一个AggregateException . 有没有办法递归地平展所有内部聚合异常? 通常,我会使用句柄委托来处理这些,但如果有另一个内部聚合,它会返回 false。 我处理得不好吗?

编辑:由于我已经在调用Flatten,因此问题似乎是直到稍后在调用堆栈中才被捕获。 这是我调用 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.<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<---

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

聚合的平展处理异常

请记住

,"flatten"方法将为您提供异常列表,但仍可以在每个异常中留下扁平化的InnerException。

所以我发现这还不够:

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:连接尝试失败,因为连接方在一段时间后未正确响应,或建立的连接失败,因为连接的主机无法响应 192.168.42.55:443 at System.Net.Sockets.Socket.EndConnect(IAsyncResult asyncResult) at System.Net.ServicePoint.ConnectSocketInternal(Boolean connectFailure, Socket s4, Socket s6, Socket&socket, IPAddress& address, ConnectSocketState state, IAsyncResult asyncResult, Exception& exception) ---内部异常堆栈跟踪结束--- at System.Net.HttpWebRequest.EndGetRequestStream(IAsyncResult asyncResult, TransportContext& context) at System.Net.Http.HttpClientHandler.GetRequestStreamCallback(IAsyncResult ar) ---内部异常堆栈跟踪结束---

最终会像这样:

发送请求时出错。

修复程序是这样的:

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

希望对某人有所帮助。

是的,这正是您的要求:

AggreggateException.Flatten()

将遍历并将所有内容压缩为单个 AggregateException。 因此,您可以使用它来遍历所有内部异常,如下所示:

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

这是一个老问题,但 OP 遇到的问题是 await 不会从等待的任务中暴露 AggregateException,而只是 AggregateException 中的第一个异常。 因此,catch(AggregateException ex)块被绕过,异常被进一步捕获堆栈。 所以代码应该是"简单的":

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免受await行为的影响,这样你就可以得到一个包含AggregateException的AggregateException,并等待"返回"原始/内部AggregateException。

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

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

通常,AggregateException 用于将多个故障合并到单个可抛出的异常对象中。

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;