Flattening of AggregateExceptions for Processing

ぃ、小莉子 提交于 2019-11-29 03:07:59
Sean U

Yes, there's exactly what you're asking for:

AggreggateException.Flatten()

will go through and compress everything down to a single AggregateException. So you can use it to loop through the all the inner exceptions like this:

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

Keep in mind that the 'flatten' method will give you a list of Exceptions, but can still leave you with a flattened InnerExceptions inside of each Exception.

So I found that this was not really sufficient:

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

Because this exception:

System.Net.Http.HttpRequestException: An error occurred while sending the request. ---> System.Net.WebException: Unable to connect to the remote server ---> System.Net.Sockets.SocketException: A connection attempt failed because the connected party did not properly respond after a period of time, or established connection failed because connected host has failed to respond 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) --- End of inner exception stack trace --- at System.Net.HttpWebRequest.EndGetRequestStream(IAsyncResult asyncResult, TransportContext& context) at System.Net.Http.HttpClientHandler.GetRequestStreamCallback(IAsyncResult ar) --- End of inner exception stack trace ---

Would end up like this:

An error occurred while sending the request.

The fix was something like this:

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

Resulting in:

An error occurred while sending the request.

Unable to connect to the remote server

A connection attempt failed because the connected party did not properly respond after a period of time, or established connection failed because connected host has failed to respond 192.168.42.54:443

Hope that helps someone.

This is an old question, but the problem experienced by the OP is that await does not expose the AggregateException from the awaited task, but instead just the first exception in the AggregateException. So the catch(AggregateException ex) block is bypassed and the exception is caught further up the stack. So the code should have been 'simply':

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

Alternatively, Jon Skeet's WithAllExceptions extension method allows one to 'protect' the AggregateException from await's behavior by wrapping the task in another task so you get an AggregateException containing an AggregateException and await 'returns' the original/inner AggregateException.

NOTE: AggregateException.Flatten really does 'flatten' recursively, as is shown by the example on the MSDN page.

EDIT: Improved delay on retryNeeded to avoid setting a bad async example.

Normally AggregateException is used to consolidate multiple failures into a single, throwable exception object.

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

Try the sample code below, this should avoid await unwrapping the AggregateException and throw the original AggregateException at the point where task.result is called.

var task = writer.WriteLineAsync(data);
            await task.ContinueWith(t => { }, TaskContinuationOptions.ExecuteSynchronously));
            return task.Result;
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!