C# Extension methods for the AggregateException class.

using System.Collections.Generic;

namespace System
{
    /// <summary>Extension methods for AggregateException.</summary>
    public static class AggregateExceptionExtensions
    {
        /// <summary>Invokes a handler on each Exception contained by this AggregateException.</summary>
        /// <param name="aggregateException">The AggregateException.</param>
        /// <param name="predicate">
        /// The predicate to execute for each exception. The predicate accepts as an argument the Exception
        /// to be processed and returns a Boolean to indicate whether the exception was handled.
        /// </param>
        /// <param name="leaveStructureIntact">
        /// Whether the rethrown AggregateException should maintain the same hierarchy as the original.
        /// </param>
        public static void Handle(this AggregateException aggregateException, Func<Exception, bool> predicate, bool leaveStructureIntact)
        {
            if (aggregateException == null) throw new ArgumentNullException(nameof(aggregateException));
            if (predicate == null) throw new ArgumentNullException(nameof(predicate));

            // If leaveStructureIntact, use this implementation
            if (leaveStructureIntact)
            {
                var result = HandleRecursively(aggregateException, predicate);
                if (result != null)
                {
                    throw result;
                }
            }
            // Otherwise, default back to the implementation on AggregateException
            else
            {
                aggregateException.Handle(predicate);
            }
        }

        private static AggregateException HandleRecursively(AggregateException aggregateException, Func<Exception, bool> predicate)
        {
            // Maintain a list of exceptions to be rethrown
            List<Exception> innerExceptions = null;

            // Loop over all of the inner exceptions
            foreach (var inner in aggregateException.InnerExceptions)
            {
                // If the inner exception is itself an aggregate, process recursively
                var innerAsAggregate = (AggregateException) inner;
                if (innerAsAggregate != null)
                {
                    // Process recursively, and if we get back a new aggregate, store it
                    var newChildAggregate = HandleRecursively(innerAsAggregate, predicate);
                    if (newChildAggregate != null)
                    {
                        if (innerExceptions != null)
                        {
                            innerExceptions = new List<Exception>();
                        }

                        innerExceptions.Add(newChildAggregate);
                    }
                }
                // Otherwise, if the exception does not match the filter, store it
                else if (!predicate(inner))
                {
                    if (innerExceptions != null)
                    {
                        innerExceptions = new List<Exception>();
                    }

                    innerExceptions.Add(inner);
                }
            }

            // If there are any remaining exceptions, return them in a new aggregate.
            return innerExceptions.Count > 0
                ? new AggregateException(aggregateException.Message, innerExceptions)
                : null;
        }
    }
}