My Async Extensions

This post is the first in a series which will discuss an effort to formalize practices and patterns I was using over time 
into a NuGet Library called Bnaya.CSharp.AsyncExtensions.

This effort is hosted as Open Source hosted on GitHub.

On the first post I will cover better formatting of the call stack, when exception thrown from async methods.

When you’re using async / await your code is actually broken into quite complex state machine, by the compiler (more about it at this post you may also want to check this post as background).

This means that the actual call stack is very different from what you’d wrote in your code. Therefore, when you log exception you might have difficult time to decipher it into something that is pleasant to read.

For example the following code:

Will produce the following output:

System.ArgumentException: some error
at Bnaya.Samples.Program.<B>d__3.MoveNext() in C:\Code\GIT\Async\BeyondAsyncAwait\06 Readable Async Exception\Program.cs:line 42
--- 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 Bnaya.Samples.Program.<A>d__2.MoveNext() in C:\Code\GIT\Async\BeyondAsyncAwait\06 Readable Async Exception\Program.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 Bnaya.Samples.Program.<Exec>d__1.MoveNext() in C:\Code\GIT\Async\BeyondAsyncAwait\06 Readable Async Exception\Program.cs:line 25

The bold part related to your actual code, other lines related to the 
state machine re-wrote by the compiler.

By using simple extension method from Bnaya.CSharp.AsyncExtensions NuGet will format it in more readable form.

When the NuGet installed you can change the Trace line into

ex.Format()

It will produce the following output:

Root cause:
some error
Formatted Stacks
# Throw (ArgumentException): some error
Bnaya.Samples.Program.B(?) at Program.cs line 41 ->
-----.-------.-------.A(?) at Program.cs line 36 ->
-----.-------.-------.Exec(?) at Program.cs line 25 ->
====================== FULL INFORMATION ============================
System.ArgumentException: some error
at Bnaya.Samples.Program.<B>d__3.MoveNext() in C:\Code\GIT\Async\BeyondAsyncAwait\06 Readable Async Exception\Program.cs:line 41
--- 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 Bnaya.Samples.Program.<A>d__2.MoveNext() in C:\Code\GIT\Async\BeyondAsyncAwait\06 Readable Async Exception\Program.cs:line 36
--- 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 Bnaya.Samples.Program.<Exec>d__1.MoveNext() in C:\Code\GIT\Async\BeyondAsyncAwait\06 Readable Async Exception\Program.cs:line 25
====================================================================

The bold part is the formatted call stack, after the bold section it print the real call stack. If you want to omit the real stack you can alter the options like the following snippet:

ex.Format(ErrorFormattingOption.FormatDuplication)

you can choose to combine different options, for example:

ex.Format(ErrorFormattingOption.FormatDuplication | 
ErrorFormattingOption.IncludeLineNumber));

This line would produce the following output:

Root cause:
some error
Formatted Stacks
# Throw (ArgumentException): some error
Bnaya.Samples.Program.B(?) at Program.cs line 43 ->
-----.-------.-------.A(?) at Program.cs line 38 ->
-----.-------.-------.Exec(?) at Program.cs line 25 ->

Options:

  • FormatDuplication: write — — when the class namespace repeat it self.
  • IncludeLineNumber: includes the line number.
  • IncludeFullUnformattedDetails: include the real call stack.