日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

【转】2.3async中必须始终返回Task(@Ron.liang)

發(fā)布時間:2023/12/10 编程问答 33 豆豆
生活随笔 收集整理的這篇文章主要介紹了 【转】2.3async中必须始终返回Task(@Ron.liang) 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

Asp.Net Core 輕松學-經常使用異步的你,可能需要看看這個文章

目錄

  • 前言
  • 1. 異常的發(fā)生來得太突然
  • 2. 問題所在
  • 3. 問題的解決方案

?

前言

事情的起因是由于一段簡單的數(shù)據(jù)庫連接代碼引起,這段代碼從語法上看,是沒有任何問題;但是就是莫名其妙的報錯了,這段代碼極其簡單,就是打開數(shù)據(jù)庫連接,讀取一條記錄,然后立即更新到數(shù)據(jù)庫中。但是,慘痛的事實證明,老司機也是會翻車的。

1. 異常的發(fā)生來得太突然

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();}

這是一段不太標準的異步接口,可能你也這么寫過, 從結構和語法上看,這段代碼沒有任何問題,而且正常情況下,它還能執(zhí)行成功

1.2 報錯信息

從報錯信息中可以看出,數(shù)據(jù)庫上下文對象被銷毀了,是在什么時候銷毀的呢,通過跟蹤程序,了解到,是在 this.context.Update(topic); ,調用 Update 后執(zhí)行了 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 再次執(zhí)行程序,查看結果

通過輸出結果紅色方框處可以看到,確實是在執(zhí)行了 Update 以后執(zhí)行了 Dispose 方法,關于這點,如果我們使用了同步方法,先 Update 再 SaveChanges ,這是沒有任何問題的,理論上說,EFCore 中啟用了 AutoDetectChangesEnabled,我們在上面的代碼中其實無需調用 Update,直接 SaveChangesAsync 即可,也不會拋出異常,同理,如果是在同步方法中,先執(zhí)行 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);}
  • 輸出結果

從輸出結果可知,先執(zhí)行了 Update,然后執(zhí)行了 SaveChanges 輸出 affrows,最后執(zhí)行了 Dispose 方法

2. 問題所在

那到底是什么問題引起了程序執(zhí)行的不確定性呢,答案就是 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);}

細心的你已經發(fā)現(xiàn),這段代碼和 1.1 之中的沒有太多的不同,無非是增加了一些跟蹤信息,其中,最關鍵的是:增加了返回值為:Task ,替換了 void

2.2 再次執(zhí)行修正的程序

輸出結果和 1.5 中的同步方法完全相同,至此,問題解決

3. 問題的解決方案

3.1 問題分析

為什么會發(fā)生這種問題呢,原因就是因為使用了異步方法 async/await 時,當沒有值需要返回時,使用了 void 造成的,正確的做法是如果沒有返回值,則返回 Task,如果有返回值,則使用 Task?;當一個異步方法內部沒有返回 Task 的時候,基于任務的異步模式(TAP)并不知道異步任務的狀態(tài),當 this.context.Update 執(zhí)行完成后,發(fā)現(xiàn)掛載在內存中的連接已經沒有使用,就執(zhí)行了回收;實際上,此時程序還沒有執(zhí)行完成,但是 TAP 并不知道,所以它不會去阻止這個回收的過程(使用標記),所以 async/await 應該成對出現(xiàn),并且應該始終返回 Task 或者 Task,以確保 TAP 能夠將上下文進行正確的掛載,否則,當異常發(fā)生時,TAP 無非將異常信息掛載到相應的 Task 上,亦無法跟蹤其執(zhí)行狀態(tài)等信息

3.2 解決方案

請牢記下面的鐵律

  • 3.2.1 在 EFCore 中,應當始終發(fā)揮 AutoDetectChangesEnabled 的特性,不要再更新實體的時候去調用 Update 方法
  • 3.2.2 使用 async/await 修飾方法時,應該始終返回 Task 或者 Task
  • 適當?shù)氖褂猛椒椒?#xff0c;可避免異步踩坑

演示代碼下載

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

總結

以上是生活随笔為你收集整理的【转】2.3async中必须始终返回Task(@Ron.liang)的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內容還不錯,歡迎將生活随笔推薦給好友。