DbConnection.Open()有效,但dbConnection.OpenAsync()不起作用

时间:2013-05-16 09:32:44

标签: .net sql-server asp.net-mvc asynchronous

这是一个艰难的。使用完全相同的查询字符串,完全相同的以下代码:

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

从Windows应用程序运行时可以正常工作。但是,当从IIS Express或IIS 7运行时,它将永远停留在await OpenAsync()。如果我用db.Open()替换该行,它可以工作。有什么建议吗?

3 个答案:

答案 0 :(得分:5)

在ASP.NET中需要谨慎对待

await,因为同步上下文想要为单个请求序列化工作。您发布的代码本身可能很好 - await不会阻止。但是,我希望在调用链的某个位置,您可以调用.Wait()或访问.Result,而不是await

这里有几个选项:

  • 不要使用.Wait().Result(或类似的) - 而是仅使用await并使其成为正确的异步操作
  • 或使用.ConfigureAwait(false)告诉它忽略同步上下文;不幸的是,这需要添加到所有await的地方,即

    await db.OpenAsync().ConfigureAwait(false);
    var results = await db.ExecuteSomethingAsync(...).ConfigureAwait(false);
    
  • 或者,只使用同步代码 - 在大多数情况下,sql将快速运行非常 - 因此推送异步不一定可以帮助你可能认为;显然这可能因使用情况而异;但要记住的一个关键点是,ASP.NET已经具有固有的线程 - 这并不是说整个服务器在这里停止运行

答案 1 :(得分:5)

正如其他人所提到的,首先确保您的层次结构没有WaitResult次调用。一系列async方法在一个依赖于框架的入口点结束。在UI / WebForms应用程序中,这通常是async void事件处理程序。在WebAPI / MVC应用程序中,这通常是async操作。

要检查ASP.NET的另外两件事是:

  • 确保您的目标平台是.NET 4.5。
  • 确保UseTaskFriendlySynchronizationContext设置为true

如果您需要在多个平台上的共享库中支持async,您可能会发现Microsoft.Bcl.Async NuGet库非常有用。

答案 2 :(得分:0)

我注意到在ASP.Net Web Forms代码隐藏中编写async / await代码时要记住的另一个要点,那就是确保页面声明中的Async =“true”。默认情况下,此属性为false。

如果您不这样做,那么您可以看到您的页面处于永久加载状态。

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

另外,在.Net 4.5中,根据Stephen的回答,我们需要在web config appsettings部分将'UseTaskFriendlySynchronizationContext'设置为true。另一个有用的appsetting是 AllowAsyncDuringSyncStages ,对于Webforms代码隐藏中的async / await代码,它需要为false。默认情况下,这些设置均为false。

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

我在ASP.Net WebForm中有以下示例async / await代码,该代码使用上述设置非常快速地运行并且使用像Stephen建议的那样一直等待。如果没有遵循这些建议,那么您可以在浏览器中看到Webforms页面永远加载。

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;
}