.Net 開發非同步執行 ServiceProvider 寫入資料庫失敗問題

Contents
最近在專案中遇到一個有趣又棘手的問題:當我需要儲存多筆 NText 資料時,因為單次寫入會花上 3 秒以上,為了提升 API 回應速度,決定先回傳 response,再用非同步方式將資料寫入資料庫。沒想到偶爾(極低機率)會寫入失敗,而且 log 也沒有任何錯誤訊息。這到底是什麼原因?
操作流程與問題重現
- 先用
Task.Run
包裝資料庫寫入邏輯,讓 API 可以先回應。 - 非同步執行時偶爾會失敗,且沒有任何錯誤訊息。
- 嘗試加上延遲(例如
await Task.Delay(10000);
)後,錯誤就 100% 發生。
範例程式碼
|
|
小提示
記得在 Task 內部加上 try-catch,否則例外不會被正確記錄到 log!
參考:如何正確捕捉 Task 例外 | .NET 臉笑維 - 點部落
錯誤訊息與踩雷心得
當我加上延遲後,終於看到錯誤訊息:
|
|
這代表什麼?原來 response 回傳後,IServiceProvider 已經被釋放,導致非同步任務無法再使用原本的 ServiceProvider。
解決方案:使用 IServiceScopeFactory
要在非同步背景下正確存取 DI 服務,必須改用 IServiceScopeFactory
產生新的 scope。這樣就算原本的 ServiceProvider 被釋放,新的 scope 依然可以正常運作。
修正版程式碼
|
|
小提示
IServiceScopeFactory 幾乎可以解決所有這類非同步背景下的 DI 問題,記得不要直接傳遞 ServiceProvider!
詳細可參考:解决ASP.NET Core在Task中使用IServiceProvider的问题 - yi念之间 - 博客园 備份圖
感言
這次經驗讓我再次體會到 ASP.NET Core DI 生命週期的重要性。非同步背景下千萬不要直接用原本的 ServiceProvider,否則踩雷機率極高。建議大家遇到類似需求時,務必改用 IServiceScopeFactory,讓每個非同步任務都能有自己的 DI scope,資料庫寫入就能穩定又安全!