前言

事情的起因是由于一段简单的数据库连接代码引起,这段代码从语法上看,是没有任何问题;但是就是莫名其妙的报错了,这段代码极其简单,就是打开数据库连接,读取一条记录,然后立即更新到数据库中。但是,惨痛的事实证明,老司机也是会翻车的。

1. 异常的发生来得太突然

1.1 引起不舒适的代码片段

这是一段不太标准的异步接口,可能你也这么写过, 从结构和语法上看,这段代码没有任何问题,而且正常情况下,它还能执行成功

1.2 报错信息

从报错信息中可以看出,数据库上下文对象被销毁了,是在什么时候销毁的呢,通过跟踪程序,了解到,是在 this.context.Update(topic); ,调用 Update 后执行了 DbContext.Dispose(),为了证明这点,我们重写 DbContext.Dispose() 方法,并简单的输出一句话

1.3 重写 DbContext.Dispose()

1.4 再次执行程序,查看结果

通过输出结果红色方框处可以看到,确实是在执行了 Update 以后执行了 Dispose 方法,关于这点,如果我们使用了同步方法,先 Update 再 SaveChanges ,这是没有任何问题的,理论上说,EFCore 中启用了 AutoDetectChangesEnabled,我们在上面的代码中其实无需调用 Update,直接 SaveChangesAsync 即可,也不会抛出异常,同理,如果是在同步方法中,先执行 Update 再 SaveChanges ,也是没有任何问题的

1.5 同步 SaveChanges

  • 输出结果

从输出结果可知,先执行了 Update,然后执行了 SaveChanges 输出 affrows,最后执行了 Dispose 方法

2. 问题所在

那到底是什么问题引起了程序执行的不确定性呢,答案就是 async/await,我们先来尝试改进一下最初的代码

2.1 改进后的代码

细心的你已经发现,这段代码和 1.1 之中的没有太多的不同,无非是增加了一些跟踪信息,其中,最关键的是:增加了返回值为:Task ,替换了 void

2.2 再次执行修正的程序

输出结果和 1.5 中的同步方法完全相同,至此,问题解决

3. 问题的解决方案

3.1 问题分析

为什么会发生这种问题呢,原因就是因为使用了异步方法 async/await 时,当没有值需要返回时,使用了 void 造成的,正确的做法是如果没有返回值,则返回 Task,如果有返回值,则使用 Task ;当一个异步方法内部没有返回 Task 的时候,基于任务的异步模式(TAP)并不知道异步任务的状态,当 this.context.Update 执行完成后,发现挂载在内存中的连接已经没有使用,就执行了回收;实际上,此时程序还没有执行完成,但是 TAP 并不知道,所以它不会去阻止这个回收的过程(使用标记),所以 async/await 应该成对出现,并且应该始终返回 Task 或者 Task,以确保 TAP 能够将上下文进行正确的挂载,否则,当异常发生时,TAP 无非将异常信息挂载到相应的 Task 上,亦无法跟踪其执行状态等信息

3.2 解决方案

请牢记下面的铁律

  • 3.2.1 在 EFCore 中,应当始终发挥 AutoDetectChangesEnabled 的特性,不要再更新实体的时候去调用 Update 方法

  • 3.2.2 使用 async/await 修饰方法时,应该始终返回 Task 或者 Task

  • 适当的使用同步方法,可避免异步踩坑

演示代码下载

https://github.com/lianggx/EasyAspNetCoreDemo/tree/master/Ron.TaskThird

原文地址:https://www.cnblogs.com/viter/p/10271212.html


.NET社区新闻,深度好文,欢迎访问公众号文章汇总 http://www.csharpkit.com

Asp.Net Core 轻松学-经常使用异步的你,可能需要看看这个文章相关推荐

  1. Asp.Net Core 轻松学-多线程之Task快速上手

    Asp.Net Core 轻松学-多线程之Task快速上手 原文:Asp.Net Core 轻松学-多线程之Task快速上手 前言     Task是从 .NET Framework 4 开始引入的一 ...

  2. Asp.Net Core 轻松学-利用日志监视进行服务遥测

    原文:Asp.Net Core 轻松学-利用日志监视进行服务遥测 前言     在 Net Core 2.2 中,官方文档表示,对 EventListener 这个日志监视类的内容进行了扩充,同时赋予 ...

  3. Asp.Net Core 轻松学-玩转配置文件

    目录 前言 另类方式使用 hosting.json 使程序运行于多个端口 结语 前言     在 .NET Core 项目中,配置文件有着举足轻重的地位:与.NetFramework 不同的是,.NE ...

  4. Asp.NET Core 轻松学-项目目录和文件作用介绍

    前言     上一章介绍了 Asp.Net Core 的前世今生,并创建了一个控制台项目编译并运行成功,本章的内容介绍 .NETCore 的各种常用命令.Asp.Net Core MVC 项目文件目录 ...

  5. Asp.Net Core 轻松学-正确使用分布式缓存

    前言     本来昨天应该更新的,但是由于各种原因,抱歉,让追这个系列的朋友久等了.上一篇文章 在.Net Core 使用缓存和配置依赖策略 讲的是如何使用本地缓存,那么本篇文章就来了解一下如何使用分 ...

  6. 【转】1.9 Asp.Net Core 轻松学-多线程之取消令牌(

    目录 前言 1. 多线程请求合并数据源 2. 对长时间阻塞调用的异步取消令牌应用 3. CancellationToken 的链式反应 4. CancellationToken 令牌取消的三种方式 结 ...

  7. asp.net core轻松入门之MVC中Options读取配置文件

    接上一篇中讲到利用Bind方法读取配置文件 ASP.NET Core轻松入门Bind读取配置文件到C#实例 那么在这篇文章中,我将在上一篇文章的基础上,利用Options方法读取配置文件 首先注册MV ...

  8. ASP.NET Core轻松入门之Middleware管道模型

    Middleware指的是微软的的asp.net core的管道模型.其原理可以用微软官方的下图展示: 原理如上图,随着Request的发起,HttpContext会经历多个管道处理(图中的箭头游走方 ...

  9. 获取所有task_Asp.Net Core 轻松学-多线程之Task快速上手

    目录 前言 1. Task 的使用方法 3. 处理 Task 中的异常 4. 同步上下文 5. Task 的运行方式 6. 有条件的 Task 结束语 示例代码下载 前言     Task是从 .NE ...

最新文章

  1. linux终端terminal个性化配置(转)
  2. 【数理逻辑】范式 ( 合取范式 | 析取范式 | 大项 | 小项 | 极大项 | 极小项 | 主合取范式 | 主析取范式 | 等值演算方法求主析/合取范式 | 真值表法求主析/合取范式 )
  3. css类选择器或逻辑,深入理解CSS中选择器的逻辑处理
  4. Django中static media的简单配置及图片上传实践
  5. Maven - 依赖冲突
  6. apk、ipa包size优化晋级手段
  7. 【Elasticsearch】用Elasticsearch和Raspberry Pi构建一个真实世界的警报
  8. 5G 是否有过度承诺之嫌?
  9. java 泛型的类型擦除和桥方法
  10. Spring IOC源码笔记(三)
  11. android 主题是什么,什么是Android中的AppCompat主题?
  12. 学生信息管理系统之优化篇
  13. android手机内存什么东西,清理手机必须要知道,这些文件夹里都装了些什么?...
  14. 四万字长文说operator new operator delete
  15. Python实验报告
  16. SurfaceView和View的区别
  17. 小红书品牌营销白皮书(2021半年报)
  18. 搜狗校招编程题-建房子
  19. 2018-2019赛季多校联合新生训练赛第五场补题与题解(中石油)
  20. 分享一个开源的QT的串口示波器

热门文章

  1. Zabbix server is not running
  2. BZOJ4573:[ZJOI2016]大森林——题解
  3. Nginx实现tomcat集群进行负载均衡
  4. crontab定时任务中文乱码问题
  5. snmpd服务无法更改默认端口
  6. iPhone内存管理基本原则
  7. NHibernate for .NET 1.2
  8. 外观模式(Façade Pattern)
  9. CacheManager - 用 C# 编写的 .NET 的开源缓存抽象层
  10. 聊一聊基于Nacos的metadata完成服务间的AB测试