Ala*_*nut 1 c# task async-await asp.net-mvc-4 asp.net-web-api2
我有一个多层Web应用程序,最近我决定将我的服务层(在本例中为WebApi)转换为异步处理.
在这方面,我转换了所有的WebApi方法来实现任务,在MVC部分,我实现了一个业务层来调用WebApi.
我的MVC控制器只是使用业务层类来获取视图数据.
我对.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;
}
});
}
Run Code Online (Sandbox Code Playgroud)
调用此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;
}
Run Code Online (Sandbox Code Playgroud)
MVC Controller Action获取ViewModel
public async Task<ActionResult> Index()
{
_service.ActiveMenuItem = "";
var viewModel = await _service.GetIndexViewModel();
return View(viewModel);
}
Run Code Online (Sandbox Code Playgroud)
但是,一些Controller操作是PartialViewResults,因为它们是ChildActions,我无法将它们转换为索引操作等异步操作.在这种情况下我做的是:
var viewModel = _service.GetGalleryWidgetViewModel().Result;
return PartialView(viewModel);
Run Code Online (Sandbox Code Playgroud)
它是从同步方法调用异步方法的正确方法吗?
添加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();
}
}
Run Code Online (Sandbox Code Playgroud)
标准规则之一是不在Task.RunASP.NET上使用.相反,您应该使用自然异步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;
}
}
Run Code Online (Sandbox Code Playgroud)
您的服务助手大多看起来不错.提示:如果ConfigureAwait(false)在方法中使用一次,则应该在该方法的任何位置使用它.
儿童行为是当前MVC的一个难题; 没有好办法做到这一点.ASP.NET vNext MVC具有异步兼容的"ViewComponents",填补了这个空白.但是对于今天,你必须选择两个不完美的选项之一:
Task.Result并通过使用避免死锁问题ConfigureAwait(false).这种方法的问题在于,如果你不小心忘记在ConfigureAwait(false)任何地方使用它都需要使用,那么你很容易再次造成死锁(这将是异步操作完美工作但同样的代码访问的那种东西)子操作会死锁,因此单元测试可能无法捕获它并且代码覆盖率会产生误导.| 归档时间: |
|
| 查看次数: |
6084 次 |
| 最近记录: |