今天在開發程序中發現.在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環境.認為很簡單不會出問題的.做事還是太浮躁.
所以寫一篇博客,用以自省
轉載請註明出處,本文鏈接:https://www.uj5u.com/net/101964.html
標籤:.NET Core
上一篇:釘釘掃碼登錄網站(兩種方式實作)
