2

I expected that I could use this pattern:

var task = Task.Run(() => Agent.GetUserType(Instance));
await task;
string information = task.GetExceptionOrStatus(); // extension method

When my sql server is not started - just as a testcase - I see that the exception that the WCF service throws, is not handled gracefully by the task, I get:
An exception of type 'System.ServiceModel.FaultException' occurred in Microsoft.Threading.Tasks.dll but was not handled in user code.
I was under the impression that my code would be able to extract the error from the task object.

How should I do it better?

Adam Modlin
  • 2,994
  • 2
  • 22
  • 39
Gerard
  • 13,023
  • 14
  • 72
  • 125

3 Answers3

6

await will propagate any exceptions on that task, by design.

var task = Task.Run(() => Agent.GetUserType(Instance));
try
{
  await task;
}
catch (Exception ex)
{
  // TODO
}
string information = task.GetExceptionOrStatus(); // extension method
Stephen Cleary
  • 437,863
  • 77
  • 675
  • 810
  • Can it be done without try-catch when I disable `ThrowUnobservedTaskExceptions` in .net 4.5? – Gerard Feb 12 '14 at 17:28
  • 2
    @Gerard: `ThrowUnobservedTaskExceptions` has nothing to do with this behavior. Propagating the exception was the way `async` was designed; is there some reason you *don't* want to? – Stephen Cleary Feb 12 '14 at 17:56
  • It would be easy to just access the tasks result without try-catch. E.g. your `INotifyTaskCompletion` handles exceptions gracefully albeit via `ContinueWith` and a `PropertyChanged` callback. Perhaps I should look into `task.Exception.Handle`. On the other hand a try-catch is explicit which also is good. – Gerard Feb 12 '14 at 20:17
  • 2
    @Gerard: I recommend you not look at the `Task` type that way. Instead, just think of it as an asynchronous equivalent of a synchronous operation. You can use something like @svick's `IgnoreException`, but I don't recommend it; it's the asynchronous equivalent of `On Error Resume Next`, or wrapping every statement in a `try` with an empty `catch`. It's just not something you should use all the time. `NotifyTaskCompletion` is an unusual case; it purposely catches the errors and does *not* propagate them because it assumes you will handle them via data-binding. – Stephen Cleary Feb 12 '14 at 20:38
2

If you don't want await to throw, you could create a new Task based on the previous Task, that never fails. Something like:

static async Task<T> IgnoreException<T>(this Task<T> task)
{
    try
    {
        return await task;
    }
    catch
    {
        return default(T);
    }
}

Alternative implementation:

static Task<T> IgnoreException<T>(this Task<T> task)
{
    return task.ContinueWith(t => t.Exception == null ? t.Result : default(T));
}

Usage then would look like this:

await task.IgnoreException();
string information = task.GetExceptionOrStatus(); // extension method
svick
  • 236,525
  • 50
  • 385
  • 514
  • Hey ... maybe .. do you use this method ? Stephan Cleary compares it with a kind of `On Error Resume Next`. – Gerard Feb 12 '14 at 20:45
  • @Gerard I would be careful with it. If you're always going to follow it with `GetExceptionOrStatus()`, which, as the name implies, reads the exception, then I think you're fine. – svick Feb 13 '14 at 01:14
1

You should surround your await task; in a try/catch block. If an Exception occurs, it will throw an AggregateException containing all the exceptions that occurred within your Task.

Adam Modlin
  • 2,994
  • 2
  • 22
  • 39
  • 1
    An awaited task will actually have its AggregateException 'unwrapped' when you await it. If the task being awaited faults with FormatException, then you can catch FormatException directly. – Dan Bryant Feb 12 '14 at 17:12
  • Really? I've seen `AggregateException` every time I've caught an exception on an awaited method. Maybe I am misremembering... – Adam Modlin Feb 12 '14 at 17:14
  • 2
    `Task.Wait` or `Task.Result` will throw the AggregateException, but awaiting it will not. In fact, even if there are multiple exceptions in the AggregateException, it will only throw the first one. To get the underlying aggregate exception, you need to query it from the task directly after an exception occurs. See [this question](http://stackoverflow.com/questions/18314961/i-want-await-to-throw-aggregateexception-not-just-the-first-exception) for additional information. – Dan Bryant Feb 12 '14 at 17:17
  • It seems that the exception I catch has all aggregate-exceptions in one message seperated with \n\n, no inner-exceptions. – Gerard Feb 12 '14 at 17:53
  • Are you talking about if you call `ToString` on the exception? – Adam Modlin Feb 12 '14 at 18:23