How to get the original exception when using ContinueWith ()?

Consider the following code:

using System;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;

namespace Demo
{
    static class Program
    {
        static void Main()
        {
            var tasks = new Task[1];

            tasks[0] = Task.Run(() => throwExceptionAfterOneSecond())
                .ContinueWith(task => {
                    Console.WriteLine("ContinueWith()"); }, TaskContinuationOptions.NotOnFaulted);

            try
            {
                Task.WaitAll(tasks);
            }

            catch (AggregateException ex)
            {
                Console.WriteLine("Exception received: " + ex.InnerExceptions.Single().Message);
            }
        }

        static void throwExceptionAfterOneSecond()
        {
            Thread.Sleep(1000);
            throw new InvalidOperationException("TEST");
        }
    }
}

This gives the following result:

Exception received: A task was canceled.

My question is simple: how do I get the source InvalidOperationException("TEST");, not System.Threading.Tasks.TaskCanceledException?

Please note that if you delete the part .ContinueWith(), this works as I expected, and the output in this case Exception received: TEST.

(Also note that in this example .Net 4.5 is used, but the source code must use .Net 4.0)


Decision

Thanks to the answers, now it works. I chose the following solution: I had to wait for both the original task and the continuation task:

using System;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;

namespace Demo
{
    static class Program
    {
        static void Main()
        {
            var tasks = new Task[2];

            tasks[0] = Task.Run(() => throwExceptionAfterOneSecond());

            tasks[1] = tasks[0].ContinueWith(task => {
                if (task.Status == TaskStatus.RanToCompletion)
                    Console.WriteLine("ContinueWith()"); });
            try
            {
                Task.WaitAll(tasks);
            }

            catch (AggregateException ex)
            {
                Console.WriteLine("Exception received: " + ex.InnerExceptions.Single().Message);
            }

            Console.WriteLine("Done.");
        }

        static void throwExceptionAfterOneSecond()
        {
            Thread.Sleep(1000);
            throw new InvalidOperationException("TEST");
        }
    }
}
+4
source share
3 answers

Task.Run(() => throwExceptionAfterOneSecond()), Exception. , . .

TaskContinuationOptions.NotOnFaulted, . .

.ContinueWith(task => {
     if (task.Status == RanToCompletion) Console.WriteLine("ContinueWith()");
}
+2

catch, . .

tasks[0] = Task.Run(() => throwExceptionAfterOneSecond())
    .ContinueWith(task =>
    {
        if (task.IsFaulted)
        {
            // Throw the inner exception
            throw task.Exception.InnerException;
        }

        Console.WriteLine("ContinueWith()");
    });
+2

ContinueWith , . :

var tasks = new Task[1];
tasks[0] = Task.Run(() => throwExceptionAfterOneSecond());

// For error handling.
tasks[0].ContinueWith(task =>
    {
        // Your logic to handle the exception goes here

        // Aggregate exception
        Console.WriteLine(task.Exception.Message);

        // Inner exception, which is your custom exception
        Console.WriteLine(task.Exception.InnerException.Message);
    },
    TaskContinuationOptions.OnlyOnFaulted);

// If it succeeded.
tasks[0].ContinueWith(task => 
{
    // success
    Console.WriteLine("ContinueWith()");
},TaskContinuationOptions.OnlyOnRanToCompletion);
+1

All Articles