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 yields the following output:
Exception received: A task was canceled.
My question is simple: How do I get at the original InvalidOperationException("TEST");
rather than a System.Threading.Tasks.TaskCanceledException
?
Note that if you remove the .ContinueWith()
part, this works as I expected and the output in that case is Exception received: TEST
.
(Also note that this example is using .Net 4.5, but the original code must use .Net 4.0)
SOLUTION
Thanks to the answers, this is now working. I chose the following solution - I needed to wait on 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");
}
}
}
You need to store a reference to Task.Run(() => throwExceptionAfterOneSecond())
so that you can later examine it's Exception
property. This is the only task that faulted. Examining any other task will not provide that exception.
I'd also not rely on TaskContinuationOptions.NotOnFaulted
because this pretty much forces using exceptions for control flow. It is hard to wait for a non-normally completed task without an exception being thrown.
.ContinueWith(task => {
if (task.Status == RanToCompletion) Console.WriteLine("ContinueWith()");
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With