Asp.Net Core 轻松学-经常使用异步的你,可能需要看看这个文章
内容导读
互联网集市收集整理的这篇技术教程文章主要介绍了Asp.Net Core 轻松学-经常使用异步的你,可能需要看看这个文章,小编现在分享给大家,供广大互联网技能从业者学习和参考。文章包含2875字,纯文字阅读大概需要5分钟。
内容图文
![Asp.Net Core 轻松学-经常使用异步的你,可能需要看看这个文章](/upload/InfoBanner/zyjiaocheng/1228/af91e0cb22504ce1879d3242e31efa9c.jpg)
前言
事情的起因是由于一段简单的数据库连接代码引起,这段代码从语法上看,是没有任何问题;但是就是莫名其妙的报错了,这段代码极其简单,就是打开数据库连接,读取一条记录,然后立即更新到数据库中。但是,惨痛的事实证明,老司机也是会翻车的。
1. 异常的发生来得太突然
1.1 引起不舒适的代码片段
[HttpPut]
public async void Put([FromBody] TopicViewModel model)
{
var topic = this.context.Topics.Where(f => f.Id == model.Id).FirstOrDefault();
topic.Content = model.Content;
this.context.Update(topic);
var affrows = await this.context.SaveChangesAsync();
}
这是一段不太标准的异步接口,可能你也这么写过, 从结构和语法上看,这段代码没有任何问题,而且正常情况下,它还能执行成功
1.2 报错信息
从报错信息中可以看出,数据库上下文对象被销毁了,是在什么时候销毁的呢,通过跟踪程序,了解到,是在 this.context.Update(topic); ,调用 Update 后执行了 DbContext.Dispose(),为了证明这点,我们重写 DbContext.Dispose() 方法,并简单的输出一句话
1.3 重写 DbContext.Dispose()
public class ForumContext : DbContext
{
public ForumContext(DbContextOptions<ForumContext> options) : base(options)
{
}
public DbSet<Topic> Topics { get; set; }
public DbSet<Post> Posts { get; set; }
public override void Dispose()
{
base.Dispose();
Console.WriteLine("Dispose");
}
}
1.4 再次执行程序,查看结果
通过输出结果红色方框处可以看到,确实是在执行了 Update 以后执行了 Dispose 方法,关于这点,如果我们使用了同步方法,先 Update 再 SaveChanges ,这是没有任何问题的,理论上说,EFCore 中启用了 AutoDetectChangesEnabled,我们在上面的代码中其实无需调用 Update,直接 SaveChangesAsync 即可,也不会抛出异常,同理,如果是在同步方法中,先执行 Update 再 SaveChanges ,也是没有任何问题的
1.5 同步 SaveChanges
[HttpPut]
public void Put([FromBody] TopicViewModel model)
{
var topic = this.context.Topics.Where(f => f.Id == model.Id).FirstOrDefault();
topic.Content = model.Content;
this.context.Update(topic);
Console.WriteLine("Updated");
var affrows = this.context.SaveChanges();
Console.WriteLine("affrows:{0}", affrows);
}
- 输出结果
从输出结果可知,先执行了 Update,然后执行了 SaveChanges 输出 affrows,最后执行了 Dispose 方法
2. 问题所在
那到底是什么问题引起了程序执行的不确定性呢,答案就是 async/await,我们先来尝试改进一下最初的代码
2.1 改进后的代码
[HttpPut]
public async Task Put([FromBody] TopicViewModel model)
{
var topic = this.context.Topics.Where(f => f.Id == model.Id).FirstOrDefault();
topic.Content = model.Content;
this.context.Update(topic);
Console.WriteLine("Updated");
var affrows = await this.context.SaveChangesAsync();
Console.WriteLine("affrows:{0}", affrows);
}
细心的你已经发现,这段代码和 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
内容总结
以上是互联网集市为您收集整理的Asp.Net Core 轻松学-经常使用异步的你,可能需要看看这个文章全部内容,希望文章能够帮你解决Asp.Net Core 轻松学-经常使用异步的你,可能需要看看这个文章所遇到的程序开发问题。 如果觉得互联网集市技术教程内容还不错,欢迎将互联网集市网站推荐给程序员好友。
内容备注
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 gblab@vip.qq.com 举报,一经查实,本站将立刻删除。
内容手机端
扫描二维码推送至手机访问。