如何使其异步化?(async, await - C#,MVC)

4

我刚刚尝试使我的ASP.NET MVC应用程序的部分异步执行,但即使我已经阅读并尝试了很多方法,我仍然不太理解async-await模式,并希望有人能给我一些提示。

基本上我有以下内容:

  1. A javascript call to my controller which fetches a partial View for a chart (this happens several times after page load for a lot of charts)

    // Load content of one chart
    my.loadChartContent = function (data, callback) {
    $.post("/Dashboard/GetChartContent/", data, function (datain) {
        if (isFunction(callback))
            callback(datain);
    });
    };
    
  2. A controller action which calls a database method in another class

    public ActionResult GetChartContent(int id, bool isDraft = false)
    {
        //do something
        //...
    
        var chartdata = _dataService.GetDataForChart(chart, isDraft, _user.Id); //long running query
    
        //do something with chartdata
    
        return View(chartdata);
    }
    
  3. The data class (_dataService) which fetches the data from the database with a SqlDataReader and loads a DataTable with that data.

问题在于虽然JavaScript是异步执行的,但控制器操作似乎被阻塞,直到从DataService类返回结果。我想启动所有对数据库的查询并异步等待结果,以便长时间运行的查询不会阻塞较短的查询。(在SQL Server Profiler中,我看到查询为Begin-End、Begin-End、Begin-End => 但应该是begin-begin-begin - end-end-end)
在哪里应该使用async-await?仅对控制器操作使用它是否足够,还是必须使整个“调用链”异步?
更新:当我使用SQLConnection.OpenAsync和ExecuteReaderAsync时,代码永远无法完成...我不知道原因是什么?
    public async Task<Query> GetSqlServerData(Query query)
    {
        var dt = new DataTable();
        var con = new SqlConnection(query.ConnectionString);

        await con.OpenAsync();
        var cmd = new SqlCommand(query.SelectStatement, con);
        var datareader = await cmd.ExecuteReaderAsync();

        dt.Load(datareader);

        con.Close();
        query.Result = dt;
        return query;
    }

你能给我们展示一下“长时间运行的查询”代码吗? - Ahmed KRAIEM
代码只是一个基本的cmd.ExecuteReader()和DataTable.Load(reader),用于填充数据表。这不是一个SQL问题。 - AxSc
你应该使用方法的异步版本(SqlDataReader中的BeginExecuteReaderReadAsync)。http://blogs.microsoft.co.il/blogs/bnaya/archive/2012/01/15/using-async-await.aspx - Ahmed KRAIEM
在此处检查答案:https://dev59.com/Gmcs5IYBdhLWcg3wGgNc - Ahmed KRAIEM
很遗憾,我不明白。请看我上面的更新。 - AxSc
4个回答

5
感谢大家的答案。但是真正的答案比我想象的要简单得多。Stephen通过这句话指引了我走向正确方向:
所有的HTTP调用自然都是异步的。
我知道这一点,并且通常情况下也是如此,但当你使用会话时显然不成立,因为ASP.NET MVC等待每个请求(来自同一个用户)完成以同步会话。你可以在这里找到更好的解释:http://www.stefanprodan.eu/2012/02/parallel-processing-of-concurrent-ajax-requests-in-asp-net-mvc/ 所以,只需装饰我的控制器...
[SessionState(System.Web.SessionState.SessionStateBehavior.ReadOnly)]
...就做到了,现在我有了想要的结果——同时查询我的SQL Server,并且响应时间更快。
@Stephen - 不仅限于两个并行请求 => http://www.browserscope.org/?category=network

3
问题在于,尽管JavaScript是异步执行的,但控制器操作似乎会被阻塞,直到来自DataService类的结果返回。我希望启动对数据库的所有查询并异步等待结果,以便长时间运行的查询不会阻塞较短的查询。
“异步”一词可以应用于几种不同的方式。在JavaScript中,所有内容都是异步的。所有HTTP调用都是自然异步的。
如果您没有看到任何sql重叠,则应确保多次调用loadChartContent(不要一次通过回调链接调用它或类似方法)。浏览器将限制您同时发出两个请求,但您应该看到同时有两个请求命中您的SQL服务器。
使你的服务器端异步化并不能帮助你,因为async不会改变HTTP协议(正如我在我的博客中所描述的)。即使你让你的数据库访问异步化,你的控制器动作仍然必须等待它们完成,而浏览器仍然会限制你只能有两个未完成请求。服务器端async对于扩展你的Web服务器非常有用,但是如果你的Web服务器只与单个SQL Server后端通信,那么扩展你的Web服务器没有意义,因为你的Web服务器不是你可扩展性的决定因素

另外,我怀疑你的异步代码永远无法完成是因为你正在使用ResultWait;我在我的博客中解释了这一点。

所以,回到您最初的问题:如果您想要启动所有对数据库的查询,则需要将API从“啰嗦”变为“大块”。也就是说,添加一个GetChartContents操作,该操作接受多个ID并在并行运行所有这些查询。我建议使用async数据库方法,类似于以下内容:
public async Task<ActionResult> GetChartContents(int[] ids, bool isDraft = false)
{
  var charts = ...;
  var chartTasks = Enumerable.Range(0, ids.Length)
      .Select(i => _dataService.GetDataForChartAsync(charts[i], isDraft, _user.Id))
      .ToArray();
  var results = await Task.WhenAll(chartTasks);
  ...
  return View(results);
}

0

如果你有:

public async Task<Query> GetSqlServerData(Query query)
{
    var dt = new DataTable();
    var con = new SqlConnection(query.ConnectionString);

    await con.OpenAsync();
    var cmd = new SqlCommand(query.SelectStatement, con);
    var datareader = await cmd.ExecuteReaderAsync();

    dt.Load(datareader);

    con.Close();
    query.Result = dt;
    return query;
}

然后使用:

query1 = await GetSqlServerData(query1);
query2 = await GetSqlServerData(query2);
query3 = await GetSqlServerData(query3);

0

我尝试使用这个教程,但在第一部分失败了。public async Task<ActionResult> GizmosAsync() 给出了错误提示:“异步操作方法'GetChartContent'返回一个无法同步执行的任务”。 - AxSc

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接