2

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

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

从 Windows 应用程序运行时可以工作。await OpenAsync()但是,当从 IIS Express 或 IIS 7 运行时,它会永远卡住。如果我用它替换该行,db.Open()它仍然可以工作。有什么建议么?

4

3 回答 3

6

await在 ASP.NET 中需要谨慎对待,因为同步上下文希望如何序列化单个请求的工作。您发布的代码本身可能很好-await不会阻塞。但是,我希望在调用链中的某个地方,您正在调用.Wait()或访问.Result,而不是await.

这里有几个选项:

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

    await db.OpenAsync().ConfigureAwait(false);
    var results = await db.ExecuteSomethingAsync(...).ConfigureAwait(false);
    
  • 或者,只使用同步代码——在大多数情况下,sql 会运行得非常快——所以异步推送它不一定像你想象的那样有帮助;显然,这可能因使用情况而异;但这里要记住的一个关键点是 ASP.NET 本身就已经是线程化的——这并不是说整个服务器在这里都停止了

于 2013-05-16T09:42:17.370 回答
5

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

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

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

如果您需要async在多个平台上支持共享库,您可能会发现Microsoft.Bcl.AsyncNuGet 库很有帮助。

于 2013-05-16T10:18:00.413 回答
0

我注意到在 ASP.Net Web Forms 代码隐藏中编写异步/等待代码时要记住的另一个重要点,那就是确保页面声明中的 Async="true"。此属性默认为 false。

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

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

此外,在 .Net 4.5 中,根据 Stephen 的回答,我们需要在 web config appsettings 部分将“UseTaskFriendlySynchronizationContext”设置为 true。另一个有用的应用设置是AllowAsyncDuringSyncStages ,对于 Webforms代码隐藏中的异步/等待代码,它需要为 false。默认情况下,这些设置都是错误的。

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

我在 ASP.Net WebForm 中有以下示例 async/await 代码,该代码使用上述设置运行得非常快,并按照 Stephen 的建议一直使用 await。如果不遵循这些建议,那么您可能会在浏览器中看到 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;
}
于 2015-04-03T14:40:43.810 回答