首页 文章

Asp.Net MVC,WebApi和正确的异步方法

提问于
浏览
1

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

在这方面,我转换了所有的WebApi方法来实现任务,在MVC部分,我实现了一个业务层来调用WebApi .

我的MVC控制器只是使用业务层类来获取视图数据 .

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

代码示例:

WebApi action:

[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;
                    }
                });
        }

Business class method to call this api (NewsService class in MVC app.)

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;
        }

MVC Controller Action to get ViewModel

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

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

但是,一些Controller操作是PartialViewResults,因为它们是ChildActions,我无法将它们转换为索引操作等异步操作 . 在这种情况下我做的是:

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();
        }
    }

1 回答

  • 5

    其中一个标准规则是不在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”,填补了这个空白 . 但是对于今天,你必须选择两个不完美的选项之一:

    • 使用阻止 Task.Result 并使用 ConfigureAwait(false) 避免死锁问题 . 这种方法的问题在于,如果你不小心忘记在任何地方都需要使用 ConfigureAwait(false) ,那么你很容易再次造成死锁(这将是异步操作完美工作但访问相同代码的那种事情 . 通过子操作会导致死锁,因此单元测试可能无法捕获它并且代码覆盖率会产生误导 .

    • 复制具有同步等效项的子操作所需的所有服务方法 . 这种方法也存在维护问题:服务逻辑是重复的 .

相关问题