我只是试图使我的 ASP .NET MVC 应用程序的一部分异步,但即使在阅读和尝试了很多之后,我也不是真正理解异步等待模式,希望有人能给我一个提示。
基本上我有以下几点:
-
对我的控制器的 javascript 调用,它获取图表的部分视图(在很多图表的页面加载后会发生这种情况几次(
// Load content of one chart my.loadChartContent = function (data, callback) { $.post("/Dashboard/GetChartContent/", data, function (datain) { if (isFunction(callback)) callback(datain); }); };
-
调用另一个类中的数据库方法的控制器操作
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); }
-
数据类 (_dataService(,它使用 SqlDataReader 从数据库中获取数据,并使用该数据加载数据表。
问题在于,尽管 javascript 是异步执行的,但 Controller-Actions 似乎被阻止,直到 DataService 类的结果返回。我想启动对数据库的所有查询并异步等待结果,以便长时间运行的查询不会阻止较短的查询。(在SQL Server Profiler中,我看到查询为Begin-End,Begin-End,Begin-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;
}
谢谢大家的回答。但真正的答案比我想象的要简单得多。斯蒂芬用这句话为我指出了正确的方向
所有 HTTP 调用自然都是异步的。
我知道这一点,通常确实如此,但显然不是当您使用会话时 ASP.NET 因为 MVC 会等待每个请求(来自一个用户(完成以同步会话。你可以在这里找到更好的解释:http://www.stefanprodan.eu/2012/02/parallel-processing-of-concurrent-ajax-requests-in-asp-net-mvc/
所以 - 只是用
...[SessionState(System.Web.SessionState.SessionState.SessionStateBehavior.ReadOnly(]
。做到了,现在我得到了我想要的结果 - 在我的 SQL Server 上同时查询和更快的响应时间。
@Stephen - 超过 2 个同时请求 => http://www.browserscope.org/?category=network
问题在于,尽管 javascript 是异步执行的,但 Controller-Actions 似乎被阻止,直到 DataService 类的结果返回。我想启动对数据库的所有查询并异步等待结果,以便长时间运行的查询不会阻止较短的查询。
术语"异步"可以通过几种不同的方式应用。在 JavaScript 中,一切都是异步的。所有 HTTP 调用自然都是异步的。
如果您没有看到任何 sql 重叠,那么您应该确保多次调用loadChartContent
(而不是一次通过回调或类似的东西链接调用它(。浏览器将限制您同时发出两个请求,但您应该一次看到两个请求命中您的 sql 服务器。
让你的服务器端async
不会帮助你,因为async
不会改变HTTP协议(正如我在我的博客上描述的那样(。即使您将数据库访问设为async
,您的控制器操作仍然需要等待它们完成,并且浏览器仍将限制您两个未完成的请求。服务器端async
对于缩放 Web 服务器很有用,但如果 Web 服务器与单个 SQL Server 后端通信,则缩放 Web 服务器就没有意义,因为 Web 服务器不是可伸缩性的决定因素。
附带说明一下,我怀疑您的async
代码永远不会完成的原因是因为您使用的是Result
或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);
}
我认为本教程为您尝试做的事情提供了一个很好的起点。
http://www.asp.net/mvc/tutorials/mvc-4/using-asynchronous-methods-in-aspnet-mvc-4
如果您有:
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);