我有一个获取仪表板视图的Web API方法。
该方法调用大约24个不同的查询。每个查询执行大约需要60ms,我正在使用Glimpse进行配置。
我希望这样做,是异步运行它们,以避免一个接一个地调用每一个,从而使它成为60ms X 5方法调用。
我也是Async Await的新手,所以我的期望可能不正确。
这是我的Web API方法
[HttpGet]
[ExceptionHandling]
public async Task<DashboardResponse> GetDashboard()
{
return await DashboardResponse.GetDashboard();
}
这是辅助方法
public static async Task<DashboardResponse> GetDashboard()
{
var resp = new DashboardResponse();
resp.MonthGlance = await GetMonthAtAGlance();
resp.MostRecentOrder = await GetMostRecentOrder();
resp.CreateAnOrder = await GetCreateAnOrder();
resp.RecentOrders = await GetRecentOrders();
resp.RecentNotifications = await GetRecentNotifications();
var messages = MessageResponse.GetMessages(new MessageFilters() { PageNumber = 1, PageSize = 10 }).Messages;
resp.RecentMessages.Messages = messages;
resp.OrderLineChart = GetOrderLineChart();
return resp;
}
这是辅助方法内部调用的方法之一(其余方法设置非常相似)
public static async Task<MonthGlanceResp> GetMonthAtAGlance()
{
var monthAtAGlance = new MonthGlanceResp();
var monthStart = new DateTime(DateTime.Now.Year, DateTime.Now.Month, 1);
var monthEnd = monthStart.AddMonths(1).AddDays(-1);
var monthGlanceQuery = Helpers.DbContext.Current.Orders
.Where(c => c.Status != OrderStatuses.Deleted &&
(c.OrderSubSource == OrderSubSources.ApplicantLink ||
c.OrderSubSource == OrderSubSources.BulkImport ||
c.OrderSubSource == OrderSubSources.OrderComprehensive) &&
c.DateIn >= monthStart &&
c.DateIn <= monthEnd).AsQueryable();
if (UserContext.Current.AccountAccess == CustomerAccessTypes.Global)
{
monthGlanceQuery = monthGlanceQuery.Where(c => c.CustomerId == UserContext.Current.CustomerId);
}
else if (UserContext.Current.AccountAccess == CustomerAccessTypes.Account)
{
monthGlanceQuery = monthGlanceQuery.Where(c => c.CustomerAccountId == UserContext.Current.CustomerAccountId);
}
else
{
monthGlanceQuery = monthGlanceQuery.Where(c => c.CustomerAccountPersonId == UserContext.Current.CustomerAccountPersonId);
}
monthAtAGlance.ClearOrderCount = await monthGlanceQuery
.CountAsync(c => c.OrderLineItems
.Where(d => d.ItemType == "package_service" || d.ItemType == "service")
.All(d => d.Result == OrderLineItemResults.Clear && d.Status == OrderLineItemStatuses.ApprovedForClient));
monthAtAGlance.QuestionableOrderCount = await monthGlanceQuery
.CountAsync(c => c.OrderLineItems
.Where(d => d.ItemType == "package_service" || d.ItemType == "service")
.All(d => d.Status == OrderLineItemStatuses.ApprovedForClient) &&
c.OrderLineItems
.Where(d => d.ItemType == "package_service" || d.ItemType == "service")
.Any(d => d.Result == OrderLineItemResults.Questionable));
monthAtAGlance.PendingOrderCount = await monthGlanceQuery
.CountAsync(c => c.OrderLineItems
.Where(d => d.ItemType == "package_service" || d.ItemType == "service")
.Any(d => d.Status != OrderLineItemStatuses.ApprovedForClient));
monthAtAGlance.OrderCount = await monthGlanceQuery.CountAsync();
return monthAtAGlance;
}
唯一的问题是,在实现所有异步等待更改之后,似乎web api调用现在比以前运行得慢!我不确定我的结构是否正确,或者即使我认为可能。
答案 0 :(得分:7)
唯一的问题是,在实现所有异步等待更改之后,似乎web api调用现在比以前运行得慢!
异步代码运行速度比同步代码慢,除非您引入并发。
旁注:异步代码经常在服务器应用程序(例如,ASP.NET)上使用,即使单个请求没有并发性,因为即使每个单独的请求(稍微)较慢,整个系统也可以扩展越来越快。
所以,你需要并发。
不同的查询
您可能想要考虑一下。如果你正在点击单个SQL服务器实例,你真的可以从同时执行查询中获得任何好处吗?也许,也许不是。最好先测试一下。
请注意,实体框架 - 虽然它允许异步查询 - only allows one asynchronous query at a time per context。您需要为每个并发查询创建不同的上下文。所以你需要小心并发查询,因为你的一些实体来自不同的上下文。
那就是说,可以执行如下所示的并发调用:
public static async Task<DashboardResponse> GetDashboardAsync()
{
var resp = new DashboardResponse();
var monthAtAGlanceTask = GetMonthAtAGlanceAsync();
var mostRecentOrderTask = GetMostRecentOrderAsync();
...
await Task.WhenAll(monthAtAGlanceTask, mostRecentOrderTask, ...);
resp.MonthGlance = await monthAtAGlanceTask;
resp.MostRecentOrder = await mostRecentOrderTask;
...
return resp;
}
请注意,您的代码几乎肯定无法使用Helpers.DbContext.Current
;每个并发查询都需要自己的上下文。