如何使这个异步? (异步,等待 - C#,MVC)

时间:2013-10-10 08:36:30

标签: c# asp.net asp.net-mvc asynchronous async-await

我只是尝试将我的ASP .NET MVC应用程序的一部分异步,但即使在阅读并尝试了很多之后我也不太了解async-await模式并希望有人能给我一个提示。

基本上我有以下内容:

  1. 对我的控制器进行javascript调用,获取图表的部分视图(在很多图表的页面加载后会发生这种情况几次)

    // Load content of one chart
    my.loadChartContent = function (data, callback) {
    $.post("/Dashboard/GetChartContent/", data, function (datain) {
        if (isFunction(callback))
            callback(datain);
    });
    };
    
  2. 在另一个类中调用数据库方法的控制器操作

    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. 数据类(_dataService),它使用SqlDataReader从数据库中获取数据,并使用该数据加载DataTable。

  4. 问题是虽然javascript是异步执行的,但是在DataService类返回结果之前,Controller-Actions似乎被阻止了。我想启动对数据库的所有查询并异步等待结果,以便长时间运行的查询不会阻止较短的查询。 (在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;
        }
    

4 个答案:

答案 0 :(得分:5)

谢谢大家的回答。但真正的答案比我想象的简单得多。斯蒂芬用正确的方向指出了我

  

所有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 - 超过2个同步请求=&gt; http://www.browserscope.org/?category=network

答案 1 :(得分:3)

  

问题是虽然javascript是异步执行的,但是在DataService类返回结果之前,Controller-Actions似乎被阻止了。我想启动对数据库的所有查询并异步等待结果,以便长时间运行的查询不会阻止较短的查询。

术语“异步”可以通过几种不同的方式应用。在JavaScript中,一切都是异步的。所有HTTP调用都是自然异步的。

如果你没有看到任何 sql重叠,那么你应该确定你多次调用loadChartContent(一次通过回调或任何东西链接一次不调用它)像那样)。浏览器会将您限制为两个同时发出的请求,但您应该一次看到两个请求命中您的SQL服务器。

让您的服务器端async对您无济于事,因为async doesn't change the HTTP protocol(正如我在博客中所述)。即使您使数据库访问async,您的控制器操作仍然需要等待它们完成,浏览器仍然会限制您使用两个未完成的请求。服务器端async对于扩展您的Web服务器很有用,但if your web server is talking to a single SQL Server backend, then there's no point in scaling your web server because your web server is not the determining factor in your scalability

另一方面,我怀疑the reason your async code never finishes is because you're using Result or Wait;我在我的博客上解释了这一点。

所以,回到原来的问题:如果你想开始所有对数据库的查询,那么你需要将API更改为“chunky”而不是“chatty”。即,添加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);
}

答案 2 :(得分:0)

我认为本教程为您要做的事情提供了一个很好的起点。

http://www.asp.net/mvc/tutorials/mvc-4/using-asynchronous-methods-in-aspnet-mvc-4

答案 3 :(得分: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);