【问题标题】:Web API Async, Am I Doing It Right?Web API 异步,我做对了吗?
【发布时间】:2014-09-08 13:44:27
【问题描述】:

我有一个获取仪表板视图的 Web API 方法。

该方法调用了大约 24 个不同的查询。每个查询执行大约需要 60 毫秒,我使用 Glimpse 进行分析。

我希望做的是异步运行它们,以避免一个接一个地调用它们,从而使其成为 60 毫秒 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 调用的运行速度似乎比以前慢了!我不确定我的结构是否正确,或者即使我的想法是可能的。

【问题讨论】:

  • 您正在异步执行任务,但不是并行执行,因此您实际上是在等待每个任务完成后再执行下一个任务。如果您需要异步方法的返回值作为其余方法的返回值,这很有用
  • 异步增加了一些开销,但只是一小部分。它的运行速度慢了多少?您是否以毫秒为单位处理?

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


【解决方案1】:

唯一的问题是,在实现所有异步等待更改后,现在 Web api 调用的运行速度似乎比以前慢了!

异步代码比同步代码运行得慢,除非你引入并发

旁注:即使单个请求不具有并发性,异步代码也经常用于服务器应用程序(例如,ASP.NET),因为即使每个单独的请求(稍微)慢一些,系统作为一个整体也可以扩展越来越快。

所以,你需要并发。

不同的查询

您可能需要考虑一下。如果您要访问 SQL Server 的单个实例,您真的会从并发执行查询中获得任何好处吗?也许,也许不是。最好先测试一下。

请注意,实体框架 - 虽然它允许异步查询 - 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;每个并发查询都需要自己的上下文。

【讨论】:

  • 斯蒂芬 - 你提出了一些很好的观点,我认为我真的需要重新调整我的工作方式,目前我正试图在一个上下文中运行所有查询。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-12-26
  • 2020-03-26
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多