c# - Asp.Net MVC、WebApi 和正确的异步方法

标签 c# asp.net-mvc-4 task async-await asp.net-web-api2

我有一个多层 Web 应用程序,最近我决定将我的服务层(在本例中为 WebApi)转换为异步处理。

在这方面,我将我所有的 WebApi 方法转换为实现任务,并且在 MVC 部分,我实现了一个调用 WebApi 的业务层。

我的 MVC Controller 只是使用业务层类来获取 View 数据。

我是 .Net 4.5 中基于任务的编程的新手,想知道我的方法是正确的还是有缺陷的。在我的简单测试中,我发现响应时间的性能有所提高,但我不确定我的所有异步调用是否安全或容易出错。

代码示例:

WebApi 操作:

[Route("category/withnews/{count:int=5}")]
        public async Task<IEnumerable<NewsCategoryDto>> GetNewsCategoriesWithRecentNews(int count)
        {
            return await Task.Run<IEnumerable<NewsCategoryDto>>(() =>
                {
                    using (var UoW = new UnitOfWork())
                    {
                        List<NewsCategoryDto> returnList = new List<NewsCategoryDto>();

                        var activeAndVisibleCategories = UoW.CategoryRepository.GetActiveCategories().Where(f => f.IsVisible == true);
                        foreach (var category in activeAndVisibleCategories)
                        {
                            var dto = category.MapToDto();
                            dto.RecentNews = (from n in UoW.NewsRepository.GetByCategoryId(dto.Id).Where(f => f.IsVisible == true).Take(count)
                                              select n.MapToDto(true)).ToList();

                            returnList.Add(dto);
                        }

                        return returnList;
                    }
                });
        }

调用此 api 的业务类方法(MVC 应用程序中的 NewsService 类。)

public async Task<IndexViewModel> GetIndexViewModel()
        {
            var model = new IndexViewModel();

            using (var stargate = new StargateHelper())
            {
                string categoriesWithNews = await stargate.InvokeAsync("news/category/withnews/" + model.PreviewNewsMaxCount).ConfigureAwait(false);
                var objectData = JsonConvert.DeserializeObject<List<NewsCategoryDto>>(categoriesWithNews);

                model.NewsCategories = objectData;
            }

            return model;
        }

获取 ViewModel 的 MVC Controller 操作

public async Task<ActionResult> Index()
        {
            _service.ActiveMenuItem = "";

            var viewModel = await _service.GetIndexViewModel();
            return View(viewModel);
        }

但是,一些 Controller 操作是 PartialViewResults,并且由于它们是 ChildActions,我无法将它们转换为异步操作,例如 Index 操作。我在这种情况下所做的是:

var viewModel = _service.GetGalleryWidgetViewModel().Result;
return PartialView(viewModel);

从同步方法调用异步方法是否正确?

也添加了 StargateHelper.InvokeAsync 以供引用:

public async Task<string> InvokeAsync(string path)
    {
        var httpResponse = await _httpClient.GetAsync(_baseUrl + path).ConfigureAwait(false);
        httpResponse.EnsureSuccessStatusCode();

        using (var responseStream = await httpResponse.Content.ReadAsStreamAsync())
        using (var decompStream = new GZipStream(responseStream, CompressionMode.Decompress))
        using (var streamReader = new StreamReader(decompStream))
        {
            return streamReader.ReadToEnd();
        }
    }

最佳答案

标准规则之一是不要在 ASP.NET 上使用 Task.Run。相反,您应该使用自然异步 API。

例如在您的 WebAPI 中,假设您使用的是 EF6:

public async Task<IEnumerable<NewsCategoryDto>> GetNewsCategoriesWithRecentNews(int count)
{
  using (var UoW = new UnitOfWork())
  {
    List<NewsCategoryDto> returnList = new List<NewsCategoryDto>();

    var activeAndVisibleCategories = UoW.CategoryRepository.GetActiveCategories().Where(f => f.IsVisible == true);
    foreach (var category in activeAndVisibleCategories)
    {
      var dto = category.MapToDto();
      dto.RecentNews = await (from n in UoW.NewsRepository.GetByCategoryId(dto.Id).Where(f => f.IsVisible == true).Take(count)
          select n.MapToDto(true)).ToListAsync();
      returnList.Add(dto);
    }

    return returnList;
  }
}

您的服务助手大多看起来不错。提示:如果您在某个方法中使用一次 ConfigureAwait(false),则应该在该方法中的所有地方都使用它。

子操作是当前 MVC 的一个麻烦点;没有好的方法来做它们。 ASP.NET vNext MVC 具有异步兼容的“ViewComponents”,填补了这一空白。但是今天,您必须从两个不完美的选项中选择一个:

  1. 使用阻塞 Task.Result 并通过使用 ConfigureAwait(false) 避免死锁问题。这种方法的问题在于,如果您不小心忘记在需要使用它的所有地方使用 ConfigureAwait(false),那么您很容易再次导致死锁(这将是异步操作将完美运行,但子操作访问的相同代码会死锁,因此单元测试可能无法捕获它并且代码覆盖率会产生误导。
  2. 使用同步等效项复制子操作所需的所有服务方法。这种方式还有一个维护问题:业务逻辑重复。

关于c# - Asp.Net MVC、WebApi 和正确的异步方法,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/23739853/

相关文章:

javascript - 如何将 mvc Controller actionresult 参数传递给 angularjs Controller

asp.net-mvc-3 - MVC4 Razor 中可能存在重大变化,可通过 "@:@"修复

c# - 在前提完成之前继续运行

azure - 任务并行库框架和 Azure 实例的奇怪行为

c# - 使用 RegEx 平衡匹配括号

c# - 在静态泛型方法中获取当前类型?

c# - 更改连接字符串后 ASP.NET Core 重新注册 dbcontext

c# - 从 ajax 数据创建的下拉列表中绑定(bind)数据

c# - 等待来自多个对象的任务

c# - 设置 Httpresponse 的 Response Body