EF6中慎用async void

其实正确的说,应该所有地方都慎用async void,应当尽可能的只在用作事件Event方法时才采用async void,其它地方应当将async Task作为返回值。

项目中使用的是EF6,在测试过程中,产生了一个System.NotSupportedException异常,其内容如下

A second operation started on this context before a previous asynchronous operation completed. 
Use 'await' to ensure that any asynchronous operations have completed before calling another method on this context.
Any instance members are not guaranteed to be thread safe.

提示内容已经很明确的告知我们,要通过await来顺序执行所有涉及EF的Task,在stackoverflow上也有此对应的问题。当EF中有多个涉及数据库的异步操作时,要顺序对每个请求进行await,下面分别将stackoverflow上错误和正确的写法罗列下

#region 错误会产生异常的写法
var banner = context.Banners.ToListAsync()
var newsGroup = context.NewsGroups.ToListAsync()
await Task.WhenAll(banner, newsGroup);
#endregion

#region 正确的写法
var banner = await context.Banners.ToListAsync();
var newsGroup = await context.NewsGroups.ToListAsync();
#endregion

检查了项目中相关部分的代码,发现所有异步操作都是采用了正确await的写法,并没有如stackoverflow上那样描述类似的问题写法,那问题究竟在哪呢?反复检查代码,突然发现该部分代码调用的一个async void方法代码,该代码作用是向钉钉发送消息推送,其间需要从数据库中读取当前用户对应钉钉中的id,其大致代码如下

public async void SendNotice(string uid,string message)
{
//await方式从db中,根据uid读取其在钉钉中的uid
//如果读取到,那么就调用钉钉的sdk进行消息推送
}

之所以这个通知代码会写成async void是有历史原因的,因为系统中存在大量的同步代码,以及少量的异步代码(吐槽下都9102年了,居然还有那么多人不知道Task),为了同时兼容同步以及异步代码,并且这个通知本质上也不会影响业务,所以async void代码由此产生,而在之前的使用中之所以没产生System.NotSupportedException异常的原因也很简单,之前的代码都是在所有业务数据处理完毕并保存Save之后,才会调用通知代码,而异常部分的代码是业务处理过程中,就进行了通知代码调用(理论上这是不应该的,毕竟业务数据有可能产生失败,如果失败的话,就不应该进行消息推送,只是为了偷懒,加上这是个定制项目,并不需要严格意义上的逻辑正确),而在最后调用Save时,System.NotSupportedException也就闪亮登场。

既然知道了问题产生的原因,解决起来也就简单了,新增一个返回Task的通知方法,将旧代码全部复制到新方法里去,并修改旧方法调用新方法以便兼容系统中现有代码,

public async void SendNotice(string uid,string message)
{
    await SendNoticeAsync(uid,message);
}
public async Task SendNoticeAsync(string uid,string message)
{
//await方式从db中,根据uid读取其在钉钉中的uid
//如果读取到,那么就调用钉钉的sdk进行消息推送
}

在将异常部分代码改为调用await SendNoticeAsync后,异常顺利解决。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值