Java自学者论坛

 找回密码
 立即注册

手机号码,快捷登录

恭喜Java自学者论坛(https://www.javazxz.com)已经为数万Java学习者服务超过8年了!积累会员资料超过10000G+
成为本站VIP会员,下载本站10000G+会员资源,会员资料板块,购买链接:点击进入购买VIP会员

JAVA高级面试进阶训练营视频教程

Java架构师系统进阶VIP课程

分布式高可用全栈开发微服务教程Go语言视频零基础入门到精通Java架构师3期(课件+源码)
Java开发全终端实战租房项目视频教程SpringBoot2.X入门到高级使用教程大数据培训第六期全套视频教程深度学习(CNN RNN GAN)算法原理Java亿级流量电商系统视频教程
互联网架构师视频教程年薪50万Spark2.0从入门到精通年薪50万!人工智能学习路线教程年薪50万大数据入门到精通学习路线年薪50万机器学习入门到精通教程
仿小米商城类app和小程序视频教程深度学习数据分析基础到实战最新黑马javaEE2.1就业课程从 0到JVM实战高手教程MySQL入门到精通教程
查看: 522|回复: 0

记一次EF Core DBContext在Action委托中GC异常的问题.

[复制链接]
  • TA的每日心情
    奋斗
    2024-11-24 15:47
  • 签到天数: 804 天

    [LV.10]以坛为家III

    2053

    主题

    2111

    帖子

    72万

    积分

    管理员

    Rank: 9Rank: 9Rank: 9

    积分
    726782
    发表于 2021-7-22 10:35:02 | 显示全部楼层 |阅读模式

    今天在开发过程中发现.在SaveChanges的时候偶尔会抛出异常:Cannot access a disposed object. A common cause of this error is disposing a context that was resolved from dependency injection and then later trying to use the same context instance elsewhere in your application. This may occur if you are calling Dispose() on the context, or wrapping the context in a using statement. If you are using dependency injection, you should let the dependency injection container take care of disposing context instances.

    异常说得很明显,通过依赖注入的DBContext上下文已经被其他地方Dispose了.所以无法再次Dispose.

     

     

     

     

     

     

    代码逻辑很简单,就是发送邮件后,调用委托通知tracking发送成功.然后保存到数据库.

    按理说这段代码没问题,但就是报错了.

    一开始以为是哪里没有await,所以导致task开的新线程没有被等待,从而导致提前gc.但代码就这么多,都检查过了没有遗漏的地方.排除

    后来怀疑是DBContext的生命周期问题,但DBcontext是ServiceLifetime.Scoped.  同一个request中是单例的.排除

    后来经过和同事交流,在代码结尾处加入Task.CompletedTask.等待所有线程结束.

     

    神奇的事情发生了,完美运行.不报错了.

    那这样的话,就问题就只可能是定义的Action<int> rollBack委托的问题了.

     

     

     

     

     

     猛然发现,尽管我在代码中确实加入了async/await关键字

     

     

     但是这里的异步等待,只是异步等待委托内部的操作.并不等待Action委托本身.也就是说,当我们执行委托里的方法时.开辟了一个新的线程去执行_dbContext.SaveChangesAsync()的方法.但是并没有等待它完成.

    这时候主线程会立即执行下一步,也就是返回结果给Controller层.  Return Ok()给前端.这个时候DBContex立刻就会调用Dispose.等到委托的方法调用完毕再次Dispose的时候.自然而然的就会抛出异常啦.因为他之前已经被Dispose了.

    所有解决办法很简单

    方法一 在代码结尾加入await Task.CompletedTask  等待所有线程都结束.再返回.

    方法二 讲Action<int> 换成 Func<int,Task> 并在调用委托前 await 

     

    经过这次问题,还是暴露出不少问题.

    1:对async/await 还是有认识不足的地方.基础知识不扎实,导致了对委托的错误使用.

    2:对自己的代码太过自信.没有做完整的测试.事实上 这里的代码我都没测试过就上了DEV环境.认为很简单不会出问题的.做事还是太浮躁.

    所以写一篇博客,用以自省

     

    哎...今天够累的,签到来了1...
    回复

    使用道具 举报

    您需要登录后才可以回帖 登录 | 立即注册

    本版积分规则

    QQ|手机版|小黑屋|Java自学者论坛 ( 声明:本站文章及资料整理自互联网,用于Java自学者交流学习使用,对资料版权不负任何法律责任,若有侵权请及时联系客服屏蔽删除 )

    GMT+8, 2024-12-22 18:33 , Processed in 0.060539 second(s), 29 queries .

    Powered by Discuz! X3.4

    Copyright © 2001-2021, Tencent Cloud.

    快速回复 返回顶部 返回列表