Question

I have this simple TPL code:

var t = Task.Factory.StartNew(() => { throw null; })
    .ContinueWith((ant) => { Console.WriteLine("Success"); }, 
        TaskContinuationOptions.OnlyOnRanToCompletion)
    .ContinueWith((ant) => { Console.WriteLine("Error"); }, 
        TaskContinuationOptions.OnlyOnFaulted);
t.Wait();

I get an unhandled exception:

Unhandled Exception: System.AggregateException: One or more errors occurred.
...

If i put t.Wait() in a try-catch, the exception is caught there and i know it defies the whole point of using the exception continuation. Now, if i remove the completion continuation, the exception thrown by the task is handled in the exception continuation and i don't get the above exception. Can someone throw some light on whats happening? I am using VS2010 SP1 with .NET 4.0

Was it helpful?

Solution

ContinueWith() doesn't return the original Task, it returns a Task representing the continuation. And in your case that continuation is canceled, because the original Task didn't run to completion. And because the second Task wasn't faulted, your third Task was canceled too, which is why you're getting TaskCanceledException wrapped inside AggregateException.

What you can do instead is to have one continuation, which does both actions. Something like:

var t = Task.Factory.StartNew(() => { throw null; })
    .ContinueWith(
        ant =>
        {
            if (ant.IsFaulted)
                Console.WriteLine("Error");
            else
                Console.WriteLine("Success");
        });

If you do something like this often, you could create an extension method for this (plus probably a generic version for Task<T> with Action<T> as onSuccess):

public static Task ContinueWith(
    this Task task, Action onSuccess, Action<AggregateException> onError)
{
    return task.ContinueWith(
        ant =>
        {
            if (ant.IsFaulted)
                onError(task.Exception);
            else
                onSuccess();
        });
}

Usage:

var t = Task.Factory.StartNew(() => { throw null; })
    .ContinueWith(
        () => { Console.WriteLine("Success"); },
        ex => { Console.WriteLine("Error"); });
t.Wait();

Also, this assumes you know your original Task won't be canceled. If that's not the case, that's one more case you need to handle.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top