Using Configure Await in ASP.NET

January 2018

The purpose of this post is to explain what configure await does in the web context and when to use it.

Syntax

public ConfiguredTaskAwaitable ConfigureAwait(
	bool continueOnCapturedContext
)
Parameter :
continueOnCapturedContext
   true to attempt to marshal the continuation back to the original context captured; otherwise, false.

Context

By default, when an incomplete Task is awaited, the Synchronization Context is captured. The type of context is dependent on technology you are working with. For this blog post we will be focusing on AspNetSynchronizationContext in ASP.NET.

When the task resumes, it first enters the captured context before executing the remainder of the code. In fact, AspNetSynchronizationContext guarantees your continuations will get the same HttpContext.Current even if your task continues on a different thread.  

What does Configure Await do?

The parameter name continueOnCapturedContext hints at what it does. With configure await false, when an incomplete task is awaited the current context is not captured and is not available on when the executing the method.

The following API controller shows where context is dropped.

 // GET api/values
public async Task<IEnumerable<string>> Get()
{
    //Warning bad code

    //Context is present here. So you will get data.
    var httpContext = HttpContext.Current;
    httpContext.Request;
    httpContext.User;
    httpContext.Response;
    httpContext.AllErrors;
    httpContext.Session;
    
    await DoSomethingAsync().ConfigureAwait(false); //Will drop Context here.
    //Context is null here. 
    httpContext = HttpContext.Current;
    return new List<string>();
}

public async Task DoSomethingAsync()
{
    await Task.Delay(1000);
}

The right time to use ConfigureAwait is when you know that you are not going to need the context and never in top level methods such as Controllers.

This code shows when it maybe Ok to use configure await.

// GET api/values
public async Task<IEnumerable<string>> Get()
{
    //Context is present here. So you will get data.
    var httpContext = HttpContext.Current;
    //The context will be captured here.
    await DoSomethingAsync();
    //Context is present here. Because it was capture when the task was awaited.
    
    httpContext = HttpContext.Current;
    return new List<string>();
}

public async Task DoSomethingAsync()
{
    //Context is present here.
    await DoSomethingElseAsync().ConfigureAwait(false); //Context for this function dropped here.
    //No Context here
    //HttpContext.Current is null here
}

public async Task DoSomethingElseAsync()
{
    //No Context here
    await Task.Delay(1000);
}

Using ConfigureAwait

In most cases default behavior is the right way to go. You should definitely not use ConfigureAwait when you have code after the await in the method that needs the context. For ASP.NET apps, this includes any code that uses HttpContext.Current or builds an ASP.NET response, including return statements in controller actions.

If you have a core library that’s potentially shared with desktop applications, consider using ConfigureAwait in the library code.

What about Deadlocks

Using ConfigureAwait(false) to avoid deadlocks is a dangerous practice. You would have to use ConfigureAwait(false) for every await in the transitive closure of all methods called by the blocking code, including all third- and second-party code. Using ConfigureAwait(false) to avoid deadlock is at best just a hack.

Further Reading/Resources

Async/Await - Best Practices in Asynchronous Programming - By Stephen Cleary | March 2013

Parallel Programming with .NET By Stephen Toub | June 2012

Parallel Computing - It’s All About the SynchronizationContext By Stephen Cleary | February 2011

Best practice to call ConfigureAwait for all server-side code