Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

MVC controller can't execute Async method

I have a very basic MVC controller with one action:

public class HomeController : Controller
{
    public ActionResult Index()
    {
        OpenConnection().Wait();

        return View();
    }

    private async Task OpenConnection()
    {
        var synchronizationContext = SynchronizationContext.Current;
        Debug.Assert(synchronizationContext != null);

        using (
            var connection =
                new SqlConnection(
                    @"Data Source=(localdb)\ProjectsV12;Initial Catalog=Database1;Integrated Security=True;"))
        {
            await connection.OpenAsync(); // this always hangs up                
        }
    }
}

The problem is that regular action (not async version) can't execute async methods. In my case OpenConnection() method always hangs up at await connection.OpenAsync() line.

After sometime I found two ways to make this code working.

  1. Make controller's action asynchronous

    public async Task<ActionResult> Index()
    {
        await OpenConnection();
    
        return View();
    }
    
  2. Or allow async execution without capturing original SychronizationContext - for that:

    await connection.OpenAsync();

    replace with:

    await connection.OpenAsync().ConfigureAwait(false);

So, my guess is that my initial problem was somewhere around SynchronizationContext. But SynchronizationContext.Current is not null and that makes me wonder if my guess is correct.

So, could anybody explain, why not async action in MVC controller can't syncronously execute async methods?

like image 494
davidoff Avatar asked Apr 26 '15 21:04

davidoff


1 Answers

Stephen Cleary has a good blog post about this issue and it affects both ASP.NET and desktop apps. The basic gist is that because the context (ASP.NET request context in your example) is being synchronously blocked by your explicit .Wait() call, the async Task can't run code on the context to notify that it has been completed so it deadlocks.

He also proposes the same two solutions as you (use async all the way down from the top-level controller method or change your async "library" code to not capture the context).

like image 103
David Archer Avatar answered Nov 19 '22 09:11

David Archer