Accessing the AggregateException with await

The await keyword is a new keyword in C# 5.0 which, in tandem with async keyword, allows us to easily author methods which execute asynchronously in regards to the calling code. In previous posts I’ve shown certain issues you should look out for when using these keyword. In this post we’ll look at another issue with the await keyword – how to access the AggregateException.

await Only Throws One Exception

As we saw in a previous post, await (unlike Task.Wait()) does not wrap a thrown exception in an AggregateException and instead just re-throws the exception thrown within the task. This is excellent when we are dealing with asynchronous code which only has one execution path, and as a result can only throw one exception at a time. However, a Task can be composed of several other tasks running concurrently. Each and every one of these internal tasks can throw an exception, and thus every Task might handle more than one exception (which is one of the reasons for the name AggregateException). The following code demonstrates this issue:

static void Main() 
{ 
    DoWork("abcdef").Wait();
}

static async Task DoWork(string s) 
{ 
    if (s == null) throw new ArgumentNullException("s"); 

    // Setup task 
    try 
    { 
        // Remember that a String is also an IEnumerable 
        var tasks = s.Select(c => Task.Run(() => 
        { 
            Thread.Sleep(1000); // This task is very complicated!! 
            if (c % 2 == 0) 
                throw new InvalidOperationException("Cannot process " + c); 
        })); 
        await Task.WhenAll(tasks); 
    } 
    catch (InvalidOperationException ex) 
    { 
        Console.WriteLine("Caught only the first exception: {0}", ex); 
    } 
}

What happened to AggregateException?

The above code obviously results in multiple exceptions being thrown within the different tasks created, but our code is only aware of one of these exceptions (the “first” one). Sometimes this behavior is OK as we don’t care about the other exceptions – we only want to know that something went wrong. But what if we want to access the other exceptions as well? In that case we’ll need to store the task and access its Exception property as in the following code:

static void Main() 
{ 
    DoWork("abcdef").Wait(); 
} 

static async Task DoWork(string s) 
{ 
    if (s == null) throw new ArgumentNullException("s"); 

    // Setup task
    // Remember that a String is also an IEnumerable 
    var tasks = s.Select(c => Task.Run(() => 
    { 
        Thread.Sleep(1000); // This task is very complicated!! 
        if (c % 2 == 0) 
            throw new InvalidOperationException("Cannot process " + c); 
    })); 

    Task task = Task.WhenAll(tasks); 
    try 
    { 
        await task; 
    } 
    catch (InvalidOperationException ex) 
    { 
        Console.WriteLine("Caught first exception: {0}", ex); 
        Console.WriteLine("***************************"); 
        Console.WriteLine("Aggregate exception is: {0}", task.Exception); 
    } 
}

Conclusion

When using the await keyword you should be aware that you’ll only catch a single exception out of possibly more than one thrown. If it matters, take action to store the Task instance and access its “Exception” property.

Similar Posts

Leave a Reply

Your email address will not be published. Required fields are marked *