Post

Raising Multiple Exceptions with AggregateException

There are occasions where you are aware of multiple exceptions within your code that you want to raise together in one go. Perhaps your code makes a service call to a middleware orchestration component that potentially returns multiple exceptions detailed in its response or another scenario might be a batch processing task dealing with many items in one process that requires you to collate all exceptions until the end and then thrown them together.

Lets look at the batch scenario in more detail. In this situation if you raised the first exception that you found it would exit the method without processing the remaining items. Alternatively you could store the exception information in a variable of some sort and once all items are processed use the information to construct an exception and throw it. Whilst this approach works there are some drawbacks. There is extra effort required to create a viable storage container to hold the exception information and this may mean modifying existing code to not throw an exception but instead to log the details in this new ‘exception detail helper class’. This solution also lacks the additional benefits you get with creating an exception at that point in time, for example the numerous intrinsic properties that exist within Exception objects that  provide valuable additional context information to support the message within the exception. Even when all the relevant information has been collated into a single exception class then you are still left with one exception holding all that information when you may need to handle the exceptions individually and pass them off to existing error handling frameworks which rely on a type deriving from Exception.

Luckily included in .Net Framework 4.0 is the simple but very useful AggregateException class which lives in the System namespace (within mscorlib.dll). It was created to be used with the Task Parallel Library and it’s use within that library is described on MSDN here. Don’t think that is it’s only use though, as it can be put to good use within your own code in situations like those described above where you need to throw multiple exceptions, so let’s see what it offers.

The AggregateException class is an exception type, inheriting from System.Exception, that acts a wrapper for a collection of child exceptions. Within your code you can create instances of any exception based type and add them to the AggregateException’s collection. The idea is a simple one but the AggregateException’s beauty comes in the implementation of this simplicity. As it is a regular exception class it can be handled in the usual way by existing code but also as a special exception collection by the specific code that actually cares about all the exceptions nested within it’s bowels.

The class accepts the child exceptions on one of it’s seven constructors and then exposes them through it’s InnerExceptions property. Unfortunately this is a read-only collection and so it is not possible to add inner exceptions to the AggregateException after it has been instantiated (which would have been nice) and so you will need to store your exceptions in a collection until you’re ready to create the Aggregate and throw it:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// create a collection container to hold exceptions  
List<Exception> exceptions = new List<Exception>();

// do some stuff here ........

// we have an exception with an innerexception, so add it to the list  
exceptions.Add(new TimeoutException("It timed out", new ArgumentException("ID missing")));

// do more stuff .....

// Another exception, add to list  
exceptions.Add(new NotImplementedException("Somethings not implemented"));

// all done, now create the AggregateException and throw it  
AggregateException aggEx = new AggregateException(exceptions);  
throw aggEx;  

The method you use to store the exceptions is up to you as long as you have them all ready at the time you create the AggregateException class. There are seven constructors allowing you to pass combinations of: nothing, a string message, collections or arrays of inner exceptions.

Once created you interact with the class as you would any other exception type:

1
2
3
4
5
6
7
8
try  
{  
  // do task  
}  
  catch (AggregateException ex)  
{  
  // handle it  
}  

This is key as it means that you can make use of existing code and patterns for handling exceptions within your (or a third parties) codebase.

In addition to the usual Exception members the class exposes a few custom ones. The typical InnerException property is there for compatibility and this appears to return the first exception added to the AggregateException class via the constructor, so in the example above it would be the TimeoutException instance. All of the child exceptions are exposed via the InnerExceptions read-only collection property (as shown below).

The Flatten() method is another custom property that might prove useful if you find the need to nest Exceptions as inner exceptions within several AggregateExceptions. The method will iterate the InnerExceptions collection and if it finds AggregateExceptions nested as InnerExceptions it will promote their child exceptions to the parent level. This is best seen in an example:

1
2
3
4
AggregateException aggExInner = new AggregateException("inner AggEx", new TimeoutException());  
AggregateException aggExOuter1 = new AggregateException("outer 1 AggEx", aggExInner);  
AggregateException aggExOuter2 = new AggregateException("outer 2 AggEx", new ArgumentException());  
AggregateException aggExMaster = new AggregateException(aggExOuter1, aggExOuter2);  

If we create this structure above of AggregrateExceptions with inner exceptions of TimeoutException and ArgumentException then the InnerExceptions property of the parent AggregateException (i.e. aggExMaster) shows, as expected, two objects, both being of type AggregrateException and both containing child exceptions of their own:

But if we call Flatten()…

1
AggregateException aggExFlatterX = aggExMaster.Flatten();  

…we get a new ArgumentException instance returned that contains still two objects but this time the AggregrateException objects have gone and we’re just left with the two child exceptions of TimeoutException and ArgumentException:

This is a useful feature to discard the AggregateException containers (which are effectively just packaging) and expose the real meat, i.e. the actual exceptions that have been thrown and need to be addressed.

If you’re wondering how the ToString() is implemented then the aggExMaster object in the examples above (without flattening) produces this:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
System.AggregateException: One or more errors occurred. ---> System.AggregateException  
: outer 1 AggEx ---> System.AggregateException: inner AggEx --->  
System.TimeoutException: The operation has timed out. --- End of inner exception  
stack trace --- --- End of inner exception stack trace --- --- End of inner exception  
stack trace ------> (Inner Exception #0) System.AggregateException: outer 1 AggEx --->  
System.AggregateException: inner AggEx ---> System.TimeoutException: The operation  
has timed out. --- End of inner exception stack trace --- --- End of inner  
exception stack trace ------> (Inner Exception #0) System.AggregateException: inner  
AggEx ---> System.TimeoutException: The operation has timed out. --- End of inner  
exception stack trace ------> (Inner Exception #0) System.TimeoutException: The  
operation has timed out.<---<---<------> (Inner Exception #1) System.AggregateException  
: outer 2 AggEx --- System.ArgumentException: Value does not fall within the expected  
range. --- End of inner exception stack trace ------> (Inner Exception #0)  
System.ArgumentException: Value does not fall within the expected range.  

As you can see the data has been formatted in a neat and convenient way for readability, with separators between the inner exceptions.

In summary this is a very useful class to be aware of and have in your arsenal whether you are dealing with the Parallel Tasks Library or you just need to manage multiple exceptions. I like simple and neat solutions and to me this is a good example of that philosophy.

This post is licensed under CC BY 4.0 by the author.