Big*_*ddy 10 c# async-await asp.net-web-api
最近我对我在Web API项目中实现async-await模式的方式产生了怀疑.我已经读过async-await应该是"一直",这就是我所做的.但这一切都开始显得多余,我不确定我是否正确地做到了这一点.我有一个调用存储库的控制器,它调用数据访问(实体框架6)类 - "一直异步".我已经阅读了很多相互矛盾的内容,并希望将其清理干净.
编辑:引用的可能重复是一个很好的帖子,但不够具体到我的需要.我提供了代码来说明问题.看来真的很难得到一个决定性的答案.如果我们可以将async-await放在一个地方然后让.net处理其余部分,那就太好了,但我们做不到.那么,我是在做它还是不那么简单.
这是我得到的:
控制器:
public async Task<IHttpActionResult> GetMessages()
{
var result = await _messageRepository.GetMessagesAsync().ConfigureAwait(false);
return Ok(result);
}
Run Code Online (Sandbox Code Playgroud)
库:
public async Task<List<string>> GetMessagesAsync()
{
return await _referralMessageData.GetMessagesAsync().ConfigureAwait(false);
}
Run Code Online (Sandbox Code Playgroud)
数据:
public async Task<List<string>> GetMessagesAsync()
{
return await _context.Messages.Select(i => i.Message).ToListAsync().ConfigureAwait(false);
}
Run Code Online (Sandbox Code Playgroud)
如果我们可以将async-await放在一个地方然后让.net处理其余部分,那就太好了,但我们做不到.那么,我是在做它还是不那么简单.
这将是很好,如果它是简单.
样本存储库和数据代码中没有太多真实的逻辑(之后也没有await),因此可以简化它们以直接返回任务,正如其他评论者所指出的那样.
另外,示例存储库存在一个常见的存储库问题:什么都不做.如果您的真实存储库的其余部分类似,那么您的系统中可能有一个抽象级别太多.请注意,Entity Framework 已经是一个通用的工作单元存储库.
而对于async和await在一般情况下,代码往往已下班后做的await:
public async Task<IHttpActionResult> GetMessages()
{
var result = await _messageRepository.GetMessagesAsync();
return Ok(result);
}
Run Code Online (Sandbox Code Playgroud)
请记住,async并且await只是用于连接回调的花哨语法.没有一种更简单的方法可以异步表达此方法的逻辑.已经进行了一些实验,例如推断await,但是它们在这一点上都被丢弃了(我有一篇博客文章描述了为什么async/ await关键字具有他们所做的所有"愚蠢").
这种方法对于每种方法都是必要的.每个使用async/的方法await都在建立自己的回调.如果回调是不是必要的,那么该方法可以只直接返回任务,避免async/ await.其他异步系统(例如,JavaScript中的promise)具有相同的限制:它们必须一直是异步的.
从概念上讲,可以定义一个系统,在该系统中,任何阻塞操作都会自动生成线程.我反对这样一个系统的最重要的论点是它会有隐含的重入.特别是在考虑第三方库更改时,自动生成系统将是不可维护的IMO.在其签名中明确API的异步(例如,如果它返回Task,那么它是异步的)要好得多.
现在,@usr使得一个好点,也许你并不需要异步可言.例如,如果您的实体框架代码正在查询单个SQL Server实例,那几乎肯定是正确的.这是因为asyncASP.NET 的主要优点是可伸缩性,如果您不需要可伸缩性(ASP.NET部分),那么您不需要异步.请参阅我在asyncASP.NET上的MSDN文章中的"不是一个银弹"部分.
但是,我认为还有一个争论是"天然API".如果操作是自然异步的(例如,基于I/O),则其最自然的API是异步API.相反,自然同步操作(例如,基于CPU)最自然地表示为同步API.对于库来说,自然的API参数是最强的 - 如果您的存储库/数据访问层是其自己的dll,旨在在其他(可能是桌面或移动)应用程序中重用,那么它绝对应该是一个异步API.但是,如果(更可能是这种情况)它特定于这个不需要扩展的ASP.NET应用程序,那么就没有特别需要使API成为异步或同步的.
但是关于开发者体验,有一个很好的双管齐下的反驳论点.许多开发人员根本不知道他们的方式async; 代码维护者会不会搞砸它?这一论点的另一个叉的是,周围的库和工具async都还来加快速度.最值得注意的是当存在跟踪异常时缺少因果堆栈(在旁注中,我写了一个有助于此的库).此外,ASP.NET的部分内容不async兼容 - 最值得注意的是,MVC过滤器和子操作(它们正在使用ASP.NET vNext修复这两者).ASP.NET在异步处理程序的超时和线程中止方面有不同的行为 - 在学习曲线上添加更多内容async.
当然,反柜台的论点是对正在开发的开发人员的正确反应是培训他们,而不是限制可用的技术.
简而言之:
async是"一路走来".在ASP.NET上尤其如此,它不太可能很快改变.async合适或有用,取决于您和您的应用程序的场景.public async Task<List<string>> GetMessagesAsync()
{
return await _referralMessageData.GetMessagesAsync().ConfigureAwait(false);
}
public async Task<List<string>> GetMessagesAsync()
{
return await _context.Messages.Select(i => i.Message).ToListAsync().ConfigureAwait(false);
}
Run Code Online (Sandbox Code Playgroud)
如果你对异步方法的唯一调用是尾调用,那么你真的不需要await:
public Task<List<string>> GetMessagesAsync()
{
return _referralMessageData.GetMessagesAsync();
}
public Task<List<string>> GetMessagesAsync()
{
return _context.Messages.Select(i => i.Message).ToListAsync();
}
Run Code Online (Sandbox Code Playgroud)
关于你唯一丢失的是一些堆栈跟踪信息,但这很少有用.删除awaitthen而不是生成一个处理等待的状态机,你只需将被调用方法生成的任务传递回调用方法,调用方法就可以await了.
这些方法现在有时也可以内联,或者可能对它们进行尾调用优化.
如果这样做相对简单,我甚至可以将非基于任务的路径转换为基于任务的路径:
public async Task<List<string>> GetMeesagesAsync()
{
if(messageCache != null)
return messageCache;
return await _referralMessageData.GetMessagesAsync().ConfigureAwait(false);
}
Run Code Online (Sandbox Code Playgroud)
变为:
public Task<List<string>> GetMeesagesAsync()
{
if(messageCache != null)
return Task.FromResult(messageCache);
return _referralMessageData.GetMessagesAsync();
}
Run Code Online (Sandbox Code Playgroud)
但是,如果您在任何时候需要任务的结果来进行进一步的工作,那么await就可以了.
| 归档时间: |
|
| 查看次数: |
1189 次 |
| 最近记录: |