Task and Async: Magically Wrapping Returned Objects into Tasks

References

Like many developers working with Microsoft’s MVC platform, we have frequently leveraged the trio of C# language features, Task, await and async, to help improve performance of long running, I/O blocking processes in our Controller actions and Repositories.

When working with many different languages, platforms and third party tools in a consulting context, it sometimes takes a simple problem to challenge our deeper understanding of how certain API’s, libraries and language features, really work.

Multi-threaded, parallel programming remains a very a deep and challenging area of knowledge, despite the significant convenience features Microsoft has provided with the TPL (Task), await and async language features in C#. The following problem caused us to take a deeper dive into how each of these three features work together and the discovery that the Task class is essentially the same thing as a JavaScript Promise (or Futures in Microsoft-speak), a concept we are very familiar with from working with AngularJS and other front end frameworks. This post describes our discoveries around the Task, async and await language features while attempting to resolve this simple problem.

The Problem

We needed to update an existing WebAPI Controller Action to return a <strong>BadRequest</strong> based on a condition in the posted Model.

<span class="hljs-function"><span class="hljs-keyword">public</span> Task<IHttpActionResult> <span class="hljs-title">Post</span>(<span class="hljs-params">ClaimsEditViewModel model</span>)
</span>{
     <span class="hljs-comment">// Example of the code we needed to add</span>
     <span class="hljs-keyword">if</span>(model.DateReceived < DateTime.Now)
     {
         <span class="hljs-keyword">return</span> BadRequest(); <span class="hljs-comment">// PROBLEM: Compiler didn't like this (red squiggles in Visual Studio)</span>
     }

     <span class="hljs-comment">// SaveAsync is awaitable, returns Task<HttpActionResult></span>
     <span class="hljs-keyword">return</span> SaveAsync(model, <span class="hljs-string">"Index"</span>, <span class="hljs-string">"Claims"</span>, <span class="hljs-string">"Claim successfully saved."</span>); 
}

Seems like a simple task. However, the project would not build; the compiler complained about returning <strong>BadRequest</strong> (System.Web.Http.Results.ErrorMessageResult) in this method. Seems obvious at first, because the method signature requires returning a Task, not an ErrorMessageResult.

However, in the same WebApi controller, there was another Action method returning a BadRequest just fine without any complaint from the compiler:

<span class="hljs-comment">//fictional method for this example</span>
<span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">async</span> Task<IHttpActionResult> <span class="hljs-title">Post2</span>(<span class="hljs-params">ClaimsEditViewModel model, <span class="hljs-keyword">int</span> someValue</span>)
</span>{
     <span class="hljs-comment">//pseudo code</span>
    <span class="hljs-keyword">if</span> (someValue < <span class="hljs-number">0</span>) {
       <span class="hljs-keyword">return</span> BadRequest();
    }

    <span class="hljs-comment">// SaveAsync is awaitable, returns Task<HttpActionResult></span>
     <span class="hljs-keyword">return</span> <span class="hljs-keyword">await</span> SaveAsync(model, <span class="hljs-string">"Index"</span>, <span class="hljs-string">"Claims"</span>, <span class="hljs-string">"Claim successfully saved."</span>);
}

This compiled fine. Huh?

Discoveries

This gave us pause. Why could we not return a BadRequest in the first method, but return one in the second method? At first blush, both methods appeared asynchronous – both method signatures return a Task<IHttpActionResult>, and SaveAsync is obviously asynchronous, right? And BadRequest can be cast to IHttpActionResult, so what gives?

Well, a few key discoveries/refreshers:

  • C# Tasks are Futures (what?) which are Promises (ah, ok!).
  • Using Async with Task<T> enables some magic, where objects of type T are automatically wrapped in new Task objects for us. We do not have to manually wrap our objects of Type T into new Task objects before returning them.
  • This automatic wrapping happens regardless of whether or not the method is awaitable (asynchronous) or synchronous.
  • Both methods are not asynchronous. Post1 is synchronous, Post2, asynchronous. Post 1 is synchronous because returning an awaitable Task<T> like SaveAsync without the await keyword in a method returning a Task<T> without the async keyword will execute synchronously. We briefly erroneously assumed returning Task<T> meant asynchronous.
  • If the original action method could be improved: if it did not need to be asynchronous, returning a Task in a synchronous method is required and may cause a performance hit.

What follows is a paraphrased excerpt from an internal Slack thread where we walked through a deductive process to make the discovery that Task with Async automagically wraps returned objects in Tasks.

Breaking it Down – Returning BadRequest

Let’s start by validating our understanding that BadRequest can cast to IHttpActionResultin a simplified Action method with a return type of just IHttpActionResult:

<span class="hljs-function"><span class="hljs-keyword">public</span> IHttpActionResult <span class="hljs-title">Post</span>(<span class="hljs-params">ClaimsEditViewModel model</span>)
</span>{
   <span class="hljs-keyword">return</span> BadRequest();
}

This builds and compiles, so we have validated that BadRequest can be cast to the method’s return type, IHttpActionResult because of it’s parentage.

But can we return a BadRequest when the method’s return type is Task<IHttpActionResult>?

public Task<span class="hljs-tag"><<span class="hljs-name">IHttpActionResult</span>></span> Post(ClaimsEditViewModel model)
{
     return BadRequest(); // red squiggle here: BadRequest is not castable to Task<span class="hljs-tag"><<span class="hljs-name">IHttpActionResult</span>></span>
}

Another fairly obvious sanity check. This won’t work because the method now expects the BadRequest wrapped in a Task <IHttpActionResult>.

To make the above work, we need to do manually wrap the BadRequest in a Task. Let’s look at doing this using the original method to which we want to add a conditional test and return a BadRequest.

<span class="hljs-function"><span class="hljs-keyword">public</span> Task<IHttpActionResult> <span class="hljs-title">Post</span>(<span class="hljs-params">ClaimsEditViewModel model</span>)
</span>{
 <span class="hljs-comment">// Example of the code we needed to add</span>
     <span class="hljs-keyword">if</span>(model.DateReceived < DateTime.Now)
     {
          <span class="hljs-comment">// manually wrap the BadRequest in a new Task</span>
          <span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> Task<IHttpActionResult>(() => { <span class="hljs-keyword">return</span> BadRequest(); });
     }

     <span class="hljs-comment">// SaveAsync is awaitable, returns Task<HttpActionResult></span>
     <span class="hljs-keyword">return</span> SaveAsync(model, <span class="hljs-string">"Index"</span>, <span class="hljs-string">"Claims"</span>, <span class="hljs-string">"Claim successfully saved."</span>); 
}

This compiles because now every IHttpActionResult is returned, wrapped in a Task.

But wait, why do we have to wrap the BadRequest in a new Task in Post1, when we don’t have to in Post2?

<span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">async</span> Task<IHttpActionResult> <span class="hljs-title">Post2</span>(<span class="hljs-params">ClaimsEditViewModel model, <span class="hljs-keyword">int</span> someValue</span>)
</span>{

    <span class="hljs-keyword">if</span> (someValue < <span class="hljs-number">0</span>) {

       <span class="hljs-comment">// Why don't we have to wrap this in a new Task?  Why does this work?</span>
       <span class="hljs-keyword">return</span> BadRequest();

    }

    <span class="hljs-comment">// SaveAsync is awaitable, returns Task<HttpActionResult></span>
     <span class="hljs-keyword">return</span> <span class="hljs-keyword">await</span> SaveAsync(model, <span class="hljs-string">"Index"</span>, <span class="hljs-string">"Claims"</span>, <span class="hljs-string">"Claim successfully saved."</span>);
}

Obviously, BadRequest was being wrapped automagically into a Task<IHttpActionResult>. But why? What it the fact the action method is asynchronous? The fact the await keyword is used together with the async keyword?

It turns out that awaiting execution from methods invoked within the action method really has nothing to do with automagically wrapping returned objects into Tasks. It’s solely the combination of the async keyword with returned <strong>Task<T></strong> that enables this syntactical sugar.

Here, we combine async with Task<T> to automatically wrap returned objects into Tasks without any asynchronous execution. This compiles and runs.

<span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">async</span> Task<IHttpActionResult> <span class="hljs-title">PostSync</span>(<span class="hljs-params">ClaimsEditViewModel model, <span class="hljs-keyword">int</span> someValue</span>)
</span>{
            <span class="hljs-keyword">if</span> (someValue > <span class="hljs-number">0</span>)
            {
                <span class="hljs-keyword">return</span> BadRequest(); 
            }

            <span class="hljs-keyword">return</span> Save(model, <span class="hljs-string">"Index"</span>, <span class="hljs-string">"Claims"</span>);
}

With the above, Visual Studio will generate a green squiggle under PostSync and inform us that the action will execute synchronously. So it will execute synchronously despite the use of the async keyword and returning a Task. It will wrap the returned BadRequest into a Task.

So <strong>Task<T></strong> with the async keyword can be used to automagically wrap return objects of type T to Task<T>.

But what is the use of returning a Task for a synchronous method? As far as we are aware, none. In fact we suspect it might cause a performance hit (please let us know if we’re wrong).

Wrapping It Up: The Final Method with Task, Await and Async

Our original challenge was was to add a conditional check to an existing MVC Controller Action and return a BadRequest if it was met. Because the original method invoked the awaitable, asynchronous SaveAsync method, we chose to update the method to be fully asynchronous, and take advantage of the automagic wrapping of BadRequest into a Task.

<span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">async</span> Task<IHttpActionResult> <span class="hljs-title">Post</span>(<span class="hljs-params">ClaimsEditViewModel model</span>)
</span>{
     <span class="hljs-keyword">if</span> (model.DateReceived < DateTime.Now)
     {
        <span class="hljs-keyword">return</span> BadRequest(); 
     }
     <span class="hljs-keyword">return</span> <span class="hljs-keyword">await</span> SaveAsync(model, <span class="hljs-string">"Index"</span>, <span class="hljs-string">"Claims"</span>, <span class="hljs-string">"Claim successfully saved."</span>);
}
CSG Pro

CSG Pro

Our data analytics consultants solve problems and ignite victories. We help you understand your data so you can make smarter business decisions. We build custom software applications that strengthen your process and your team.
SHARE THIS
Share on twitter
Share on facebook
Share on linkedin
Share on email
RELATED POSTS

Subscribe

Subscribe to CSG. We’ll supply your inbox with the latest blog posts, training videos, and upcoming events.

Ready to wrangle your data?