Reputation: 52290
Exception handling for asynchronous methods is a little weird.
If I write a synchronous method, it's pretty clear. For example, if I do this:
public static void Test1()
{
ThrowException("Test1 has a problem");
}
public static void ThrowException(string message)
{
throw new MyException(message.ToUpper());
}
I get an exception that is relatively easy to read:
MyException thrown. Message: TEST1 HAS A PROBLEM
Trace: at Program.ThrowException(String message)
at Program.Test1()
at Program.Main()
But if I do something very similar, but async:
public static async Task Test2()
{
await ThrowExceptionAsync("Test2 has a problem");
}
public static async Task ThrowExceptionAsync(string message)
{
throw new MyException(message.ToUpper());
}
I get this mess:
System.AggregateException thrown. Message: One or more errors occurred.
Trace: at System.Threading.Tasks.Task.ThrowIfExceptional(Boolean includeTaskCanceledExceptions)
at System.Threading.Tasks.Task.Wait(Int32 millisecondsTimeout, CancellationToken cancellationToken)
at System.Threading.Tasks.Task.Wait()
at Program.Main()
MyException thrown. Message: TEST2 HAS A PROBLEM
Trace: at Program.<ThrowExceptionAsync>d__3.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at System.Runtime.CompilerServices.TaskAwaiter.GetResult()
at Program.<Test2>d__1.MoveNext()
MyException thrown. Message: TEST2 HAS A PROBLEM
Trace: at Program.<ThrowExceptionAsync>d__3.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at System.Runtime.CompilerServices.TaskAwaiter.GetResult()
at Program.<Test2>d__1.MoveNext()
This presents two problems:
I can't write a catch
block for MyException
because it gets wrapped in an AggregateException. In other words, the following does not work:
try
{
Test2().Wait();
}
catch (MyException me)
{
//This never fires
}
catch(System.AggregateException e)
{
//It'll go here instead
}
Is there a standard way of dealing with this? I'd sort of would like the exception to be easier to read, and I really would like to be able to catch MyException
instead of catching AggregateException
and searching for it.
Link to DotNetFiddle that demonstrates the issue
Upvotes: 0
Views: 1145
Reputation: 2124
await
with async
methods: it will unpack the exception, save you from the deadlock and inefficient code.Deadlock off-topic: async
method captures current synchronization context. If you'll try to wait for the completion in the same synchronization context, you'll get a deadlock.
public partial class Form1 : Form
{
public Form1() => InitializeComponent();
private void button1_Click(object sender, EventArgs e)
=> doSomthing().Wait();
private async Task doSomthing() => await Task.Delay(1000);
}
You can use ConfigureAwait
to skip capturing the context:
private void button1_Click(object sender, EventArgs e)
=> doSomthing()
.ConfigureAwait(continueOnCapturedContext: false)
.GetAwaiter()
.GetResult();
private async Task doSomthing() => await Task.Delay(1000);
This article covers this topic in depth. Feel free to read it if you are interested in the pitfalls of async
/await
.
Upvotes: 2
Reputation: 40988
You have two options:
await
. If you're using ASP.NET, there is almost never a reason to not use async
/await
through the whole stack..GetAwaiter().GetResult()
. It will get the same value as .Result
, but it will also unwrap an exception.Upvotes: 2