Web API Async,我做得对吗?

时间:2014-09-08 13:44:27

标签: c# entity-framework asynchronous asp.net-web-api async-await

我有一个获取仪表板视图的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调用现在比以前运行得慢!我不确定我的结构是否正确,或者即使我认为可能。

1 个答案:

答案 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;每个并发查询都需要自己的上下文。