When calling into async code like for example productUpdate.UpdateAsync(...)
, there are chances that it could throw an AggregateException having multiple inner exceptions or just one exception. This all depends on how UpdateAsync was implemented internally.
Question:
Since await
unwraps only the first exception in the list of exceptions within an AggregateException, the following special code tries to circumvent that, but this is clunky and ideally in every place where I am calling into some external library's async code, there could be an AggregateException with multiple exceptions. Would it make sense to have this in all those places? (sure probably could move into a helper method but that's not the point here) and also then there's the question of what meaningful things I am doing by catching these exceptions.
I think it does NOT make sense in all places. Your thoughts?
var t1 = FooAsync();
var t2 = BarAsync();
var allTasks = Task.WhenAll(t1, t2);
try
{
await allTasks;
}
catch (Exception)
{
var aggEx = allTasks.Exception as AggregateException;
// todo: do something with aggEx.InnerExceptions
}
Update: Added whole repro code here for user Dave and the result of running it:
using System;
using System.Threading.Tasks;
class Example
{
static void Main()
{
BlahAsync().Wait();
}
static async Task BlahAsync()
{
var t1 = FooAsync(throwEx: true);
var t2 = BarAsync(throwEx: true);
var allTasks = Task.WhenAll(t1, t2);
try
{
await allTasks;
}
catch (AggregateException agex)
{
Console.WriteLine("Caught an aggregate exception. Inner exception count: " + agex.InnerExceptions.Count);
}
}
static async Task FooAsync(bool throwEx)
{
Console.WriteLine("FooAsync: some pre-await code here");
if (throwEx)
{
throw new InvalidOperationException("Error from FooAsync");
}
await Task.Delay(1000);
Console.WriteLine("FooAsync: some post-await code here");
}
static async Task BarAsync(bool throwEx)
{
Console.WriteLine("BarAsync: some pre-await code here");
if (throwEx)
{
throw new InvalidOperationException("Error from BarAsync");
}
await Task.Delay(1000);
Console.WriteLine("BarAsync: some post-await code here");
}
}
Result:
FooAsync: some pre-await code here
BarAsync: some pre-await code here
Unhandled Exception: System.AggregateException: One or more errors occurred. (Error from FooAsync) ---> System.InvalidOperationException: Error from FooAsync
at Example.<FooAsync>d__2.MoveNext() in C:\Users\foo\source\repos\ConsoleApp9\ConsoleApp9\UnderstandingCallStack.cs:line 37
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at Example.<BlahAsync>d__1.MoveNext() in C:\Users\foo\source\repos\ConsoleApp9\ConsoleApp9\UnderstandingCallStack.cs:line 20
--- End of inner exception stack 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 Example.Main() in C:\Users\foo\source\repos\ConsoleApp9\ConsoleApp9\UnderstandingCallStack.cs:line 8