ASP.NET Configure Await C# with Example



ASP.NET Configure Await C# with Example

When ASP.NET handles a request, a thread is assigned from the thread pool and a request context is created. The 
request context contains information about the current request which can be accessed through the static 
HttpContext.Current property. The request context for the request is then assigned to the thread handling the 
request. 
A given request context may only be active on one thread at a time. 
When execution reaches await, the thread handling a request is returned to the thread pool while the 
asynchronous method runs and the request context is free for another thread to use. 
public async Task Index() 
{ 
// Execution on the initially assigned thread 
var products = await dbContext.Products.ToListAsync(); 
// Execution resumes on a "random" thread from the pool 
// Execution continues using the original request context. 
return View(products); 
} 
When the task completes the thread pool assigns another thread to continue execution of the request. The request 
context is then assigned to this thread. This may or may not be the original thread. 
Blocking 
When the result of an async method call is waited for synchronously deadlocks can arise. For example the 
following code will result in a deadlock when IndexSync() is called: 
public async Task Index() 
{ 
// Execution on the initially assigned thread 
List products = await dbContext.Products.ToListAsync(); 
// Execution resumes on a "random" thread from the pool 
return View(products); 
} 
public ActionResult IndexSync() 
{ 
Task task = Index(); 
// Block waiting for the result synchronously 
ActionResult result = Task.Result; 
return result; 
} 
This is because, by default the awaited task, in this case db.Products.ToListAsync() will capture the context (in 
the case of ASP.NET the request context) and try to use it once it has completed. 
 

When the entire call stack is asynchronous there is no problem because, once await is reached the original thread 
is release, freeing the request context. 
When we block synchronously using Task.Result or Task.Wait() (or other blocking methods) the original thread is 
still active and retains the request context. The awaited method still operates asynchronously and once the callback 
tries to run, i.e. once the awaited task has returned, it attempts to obtain the request context. 
Therefore the deadlock arises because while the blocking thread with the request context is waiting for the 
asynchronous operation to complete, the asynchronous operation is trying to obtain the request context in order 
to complete. 
ConfigureAwait 
By default calls to an awaited task will capture the current context and attempt to resume execution on the context 
once complete. 
By using ConfigureAwait(false) this can be prevented and deadlocks can be avoided. 
public async Task Index() 
{ 
// Execution on the initially assigned thread 
List products = await dbContext.Products.ToListAsync().ConfigureAwait(false); 
// Execution resumes on a "random" thread from the pool without the original request context 
return View(products); 
} 
public ActionResult IndexSync() 
{ 
Task task = Index(); 
// Block waiting for the result synchronously 
ActionResult result = Task.Result; 
return result; 
} 
This can avoid deadlocks when it is necessary to block on asynchronous code, however this comes at the cost of 
losing the context in the continuation (code after the call to await). 
In ASP.NET this means that if your code following a call to await someTask.ConfigureAwait(false); attempts to 
access information from the context, for example HttpContext.Current.User then the information has been lost. 
In this case the HttpContext.Current is null. For example: 
public async Task Index() 
{ 
// Contains information about the user sending the request 
var user = System.Web.HttpContext.Current.User; 
using (var client = new HttpClient()) 
{ 
await client.GetAsync("http://google.com").ConfigureAwait(false); 
} 
// Null Reference Exception, Current is null 
var user2 = System.Web.HttpContext.Current.User; 
return View(); 
 

} 
If ConfigureAwait(true) is used (equivalent to having no ConfigureAwait at all) then both user and user2 are 
populated with the same data. 
For this reason it is often recommended to use ConfigureAwait(false) in library code where the context is no 
longer used. 

0 Comment's

Comment Form

Submit Comment