Saturday, May 15, 2010

Exception handling in batch operations with the AggregateException

Doing batch operations and elegantly handling exceptions is a problem which every developer has faced before. In .NET 3.5 or older there is no out-of-the-box solution to handle exceptions in these types of scenarios, without being inconsistent to the normal flow of exception handling. .NET 4 introduces the AggregateException; an exception representing multiple exceptions. The AggregateException was introduced in the first place to be used with the parallel framework, but it can be used in other scenarios as well, such as batch operations.

Take a look at the following example..

I have a single action which I want to peform in batch. This single action might throw some exceptions.

   1:  private static void ExecuteSingleAction(int i)
   2:  {
   3:       if (i == 5)            
   4:            throw new ArgumentNullException("You forgot an argument.");            
   5:       if (i == 18)
   6:            throw new ArgumentException("This argument doesn't make sense.");              
   7:  }

Most of the exceptions the single action might throw shouldn't break my whole batch operation. While I'm executing the single actions I catch the exceptions which shouldn't break the batch operation and hold them in a list of exceptions. The AggregateException has a public constructor taking an IEnumerable of exceptions. If I catch some exceptions while executing the single action I throw an AggregateException passing in that list of exceptions to its constructor.

   1:  private static void ExecuteBatch()
   2:  {
   3:       List<Exception> exceptions = new List<Exception>();
   4:             
   5:       for (int i = 0; i < 100; i++)
   6:       {
   7:            try
   8:            {
   9:                 ExecuteSingleAction(i);
  10:            }
  11:            catch (ArgumentNullException nullRefEx)
  12:            {
  13:                 exceptions.Add(nullRefEx);
  14:            }
  15:            catch (ArgumentException argumentEx)
  16:            {
  17:                 exceptions.Add(argumentEx);
  18:            }             
  19:        }
  20:   
  21:        if (exceptions.Count > 0)           
  22:             throw new AggregateException(exceptions);            
  23:   }

In the front-end I can catch the AggregateException, run over its InnerExceptions and act based on the type of exception.

   1:  static void Main(string[] args)
   2:  {
   3:       try
   4:       {
   5:            ExecuteBatch();
   6:       }
   7:       catch (AggregateException aggEx)
   8:       {
   9:            foreach(Exception ex in aggEx.InnerExceptions)
  10:            {                  
  11:                 Console.WriteLine(ex.Message);
  12:   
  13:                 if (ex is ArgumentNullException)
  14:                 {
  15:                      //Do something
  16:                 }
  17:                 if (ex is ArgumentException)
  18:                 {
  19:                     //Do something else
  20:                 }
  21:              }
  22:       }
  23:   
  24:       Console.ReadLine();
  25:  }


The result looks like this.



More scenario's?

Can you imagine other scenarios where the AggregateException might be able to add some value?

Related post: Handling the AggregateException

5 comments:

  1. Your example doesn’t really make much sense. Why would you first catch a specific exception (ArgumentNullException) and thereafter catch a super-class (ArgumentException) of the specific exception in the ExceuteBatch method?
    If the ArgumentException is thrown, then both catch clauses will catch the exception.
    Note: public class ArgumentNullException : ArgumentException

    Perhaps an example with validation of a complex domain object model, where an AggregateException of all the invalid properties are collected/aggregated - E.g.:

    void Validate()
    {
    var exceptions = new List();

    If (string.IsEmptyOgNull(this.A))
    exceptions.Add(new ArgumentException(“A must have a value”));
    If (this.B > bMinValue)
    exceptions.Add(new ArgumentException(“B must be larger than bMinVale”));


    if (exceptions.Count > 0)
    throw new AggregateException(exceptions);
    }

    This method can be used in a similar way as the validation controls in ASP.Net, but on an object model.

    ReplyDelete
  2. That's a pretty good example.

    I'm a bit confused by the following sentences in the first alinea of your comment though: "If the ArgumentException is thrown, then both catch clauses will catch the exception. Note: public class ArgumentNullException : ArgumentException"

    To my understanding an exception can not be caught by multiple catch blocks of one try-catch construct. If I were to change places of the catch blocks, then there would be no extra value in catching both exceptions. In this example there is.

    Or is there something I misunderstand?

    ReplyDelete
  3. Anders Lybecker, I think you're wrong, in the first catch he intercepts the ArgumentNullException, therefore it will not enter the second catch anymore.
    All other ArgumentExceptions are caught by the second catch as the first catch only intercepts that specific exception and will never catch a parent exception.

    ReplyDelete
  4. Nice article Jef so don't take my statements in anyway towards yourself.

    This class is entirely pointless. It offers you some minor improvements from working with the standard exception class. Except it requires you to do a whole lot of managing exceptions inside of lists so that later you can iterate over the same list you just had effectively.

    This offers basically nothing. You can achieve the exact same result with:

    Exception exception = null

    ....
    catch(Exception ex)
    {
    exception = new Exception(ex);
    }

    Then you would just throw it at the end if you have an exception.

    You would then easily read it as such:

    var currentException = myAggregateException.InnerException;

    while(currentException != null) {

    ... do something with it

    ... move next

    currentException = myAggregateException.InnerException;


    }

    The only case I could ever see using the AggregateException class would be if I really care about reading my exceptions in a First In First Out order instead of a Last In First Out.

    ReplyDelete
  5. I don't completely disagree with your statements. You can see this class as a class which simply wraps a list of exceptions. Although it's a nice abstraction, the value it adds might be negligible. But the real value comes from the helper methods such as Flatten and Handle, which elegantly helps you processing your list of exceptions.

    Anyways, my thoughts :)

    ReplyDelete