Fixing Patterns for Async MVVM Applications

NullReferenceException

Download the code here –AsyncCommands

If you like this post you might also like an interesting post on Deadlocks while using tasks.

This post is a follow up on Stephen Cleary’s excellent post on MSDN here which is a series of articles he wrote to explain how Tasks based patterns can be effectively adopted for GUI/MVVM applications. The article is great and the attached source code works but there is one problem with the provided source code. To experience this issue open up the provided solution and within any project find the “Service.cs” class. Then comment the following statement

Figure 1
  1. //await Task.Delay(TimeSpan.FromSeconds(3), token).ConfigureAwait(false);

Run the project now and enter an invalid URL such as dhttp://msdn.microsoft.com.

You would notice that the application throws a nasty “NullReferenceException was unhandled by user code” exception and dies.

Reason

The reason for this behaviour is the way NotifyTaskCompletion class’ monitoring logic works. Essentially we pass a Task to the NotifyTaskCompletion class which then monitors the passed task and reports on its status. If however the Task that we are passing to the class is already completed or has errored this class would not be able to watch it or contain the Task’s exception. This is actually what happens here. When we write a statement such as this

Figure 2
  1. Execution = newNotifyTaskCompletion<TResult>(_command());

 

where _command is declared as

Figure 3
  1. privatereadonlyFunc<Task<TResult>> _command;

we are essentially passing a running Task to the NotifyTaskCompletion class.

When we modify the URL to be an empty URL and remove the Task.Delay statement from the Service class the Task immediately throws an exception. This does not give NotifyTaskCompletion the chance to monitor and await on the Task to catch the thrown exception.

If however we did not comment out the statement in Figure 1 the Task goes into a small delay which gives the NotifyTaskCompletion class the chance it needs to await on the Task and thus catch the exception.

Solution

The easiest way to fix this is to not pass a Task to the NotifyTaskCompletion class but pass a Func<Task<TResult>>. This would allow NotifyTaskCompletion class to ensure that the Task does not start running before the class has a chance to monitor the Task. Please note I am not stating that the class will be able to control when the Task runs (as the .NET framework would do that) but instead it can ensure that the Task does not run until at least the NotifyTaskCompletion is ready to await on it.

So with the changes this is how the NotifyTaskCompletion class will look.

Figure 4
  1. publicsealedclassNotifyTaskCompletion<TResult> : INotifyPropertyChanged
  2. {
  3.     publicTask<TResult> Task { get; privateset; }
  4.     public NotifyTaskCompletion(Func<Task<TResult>> task)
  5.     {
  6.         if (Task == null)
  7.         {
  8.             TaskCompletion = WatchTask(task);
  9.         }
  10.     }
  11.     publicTask TaskCompletion { get; set; }

You will notice that most of the code here remains unchanged. To call into the class as opposed to the statement in Figure 2 you would do this

Figure 5
  1. Execution = newNotifyTaskCompletion<TResult>(_command);

 

I have attached the modified code with this article. You can download it AsyncCommands. The bulk of the changes are in the NotifyTaskCompletion class with the AsyncCommand class modified slightly as shown in Figure 5 to pass the Func<Task<TResult>> to NotifyTaskCompletion as opposed to Task itself.

Would love to hear your thoughts so please do leave a comment here!

Leave a Comment

Your email address will not be published.