2

This is a tough one. Using the exact same query string, the exact same following code:

using (var db = new SqlConnection(queryString))
{
   await db.OpenAsync();
   var results = await db.ExecuteSomethingAsync...;
   db.Close();
{

Will work when ran from a windows application. It will however get stuck forever in await OpenAsync() when ran from IIS Express or IIS 7. If I replace that line with db.Open() it works though. Any suggestions?

2

3 Answers 3

6

await needs to be treated with caution in ASP.NET, because of how the sync-context wants to serialize the work for a single request. The code you post is probably fine by itself - the await isn't going to block. However, I expect that somewhere in the call chain for this, you are calling .Wait() or accessing .Result, rather than await.

There are a few options here:

  • don't use .Wait() or .Result (or similar) at all - instead, only use await and make it a properly async action
  • or, use .ConfigureAwait(false) to tell it to ignore sync-context; unfortunately, this would need to be added to all the places that you await, i.e.

    await db.OpenAsync().ConfigureAwait(false);
    var results = await db.ExecuteSomethingAsync(...).ConfigureAwait(false);
    
  • or, just use sync code - in most cases the sql is going to run very quickly - so pushing it async is not necessarily helping as much as you might think; obviously this may vary between usage; but a key point to keep in mind here is that ASP.NET is already inherently threaded - it isn't as though the entire server grinds to a halt here

3
  • Thanks, I am going to check these options. This code is not just used by my asp.net solution so I can't write it with just that specific scenario in mind. Commented May 16, 2013 at 9:52
  • One question though, If I can't use .Wait() or .Result, how can I capture the result of one of these async methods in a calling method that is not async itself? Or in other words, how to properly end an async chain? Commented May 16, 2013 at 9:55
  • @LuisFerrao "sync over async" - yeah, basically that isn't guaranteed to play nicely. There are 2 answers: a) add lots of ConfigureAwait; b) don't do that - or c) (yes, I know I said 2!) - make the calling method async Commented May 16, 2013 at 12:56
5

As others have mentioned, first ensure you have no Wait or Result calls up your hierarchy. A chain of async methods ends at an entry point that depends on the framework. In a UI/WebForms app, this is usually an async void event handler. In a WebAPI/MVC app, this is usually an async action.

Two other things to check for ASP.NET are:

  • Ensure your target platform is .NET 4.5.
  • Ensure you have UseTaskFriendlySynchronizationContext set to true.

If you need to support async in a shared library on multiple platforms, you may find the Microsoft.Bcl.Async NuGet library to be helpful.

0

I noticed another important point to keep in mind when writing async/await code in ASP.Net Web Forms code-behind, and that is to make sure Async="true" in the page declaration. This attribute is false by default.

If you do not do this then you could see your page in a forever-loading-state.

<%@ Page Language="C#" Async="true" %>

Also, in .Net 4.5, according to the answer by Stephen we need to have 'UseTaskFriendlySynchronizationContext' set to true in web config appsettings section. Another useful appsetting is AllowAsyncDuringSyncStages which needs to be false for async/await code in Webforms code-behind. These settings are both false by default.

<add key="aspnet:AllowAsyncDuringSyncStages" value="false" />
<add key="aspnet:UseTaskFriendlySynchronizationContext" value="true"/>

I had the following sample async/await code in an ASP.Net WebForm that ran very quickly using the above settings and using await all the way through as suggested by Stephen. If these recommendations are not followed then you could see the Webforms page loading forever in the browser.

protected async void Page_Load(object sender, EventArgs e)
{
    if (!Page.IsPostBack)
    {
        string sql = @"DELETE FROM dbo.Table1
                       WHERE Processed = 1";
        SqlConnection conn = new SqlConnection(System.Configuration.ConfigurationManager.ConnectionStrings["MainDB"].ConnectionString);
        SqlCommand cmd = new SqlCommand(sql, conn);
        int numberOfRecordsUpdated = await UpdateDatabaseAsync(conn, cmd);
    }
}
public async Task<int> UpdateDatabaseAsync(SqlConnection conn, SqlCommand cmd)
{
    int i = 0;
    try
    {
        await conn.OpenAsync();
        i = await cmd.ExecuteNonQueryAsync();
    }
    catch (Exception ex)
    {
        //log the error
        Elmah.ErrorSignal.FromCurrentContext().Raise(ex);
    }

    finally
    {
        if (conn != null)
        {
            conn.Close();
            conn.Dispose();
        }
        if (cmd != null)
        {
            cmd.Dispose();
        }
    }
    return i;
}
2
  • Not sure this applies to me as I use asp.net mvc Commented Apr 13, 2015 at 11:07
  • You can, but instead of async event handler in Webforms you would use async controller actions. You can also see an answer on async controller action in MVC at this url: stackoverflow.com/questions/20227401/…
    – Sunil
    Commented Apr 14, 2015 at 4:39

Not the answer you're looking for? Browse other questions tagged or ask your own question.