更新: 2020-02-21
補上一些憂慮
public async Task Inside() { using var scope = SqlCommonMethod.CreateTransactionScope(); await SqlCommonMethod.SetIsolationLevel(Db, IsolationLevel.Serializable); // 開啟高級別鎖 var countries = await Db.Countries.ToListAsync(); // counties 表被高級鎖了 await SqlCommonMethod.SetIsolationLevel(Db, IsolationLevel.ReadCommitted); // 設置回普通鎖, 不然接下來都會一直是高級鎖 await Inside2(); scope.Complete(); // 這里的 complete 結束並不會解鎖, 會一直等到最外面的 scope 被釋放, 符合邏輯 } public async Task Inside2() { using var scope = SqlCommonMethod.CreateTransactionScope(); var countries = await Db.States.ToListAsync(); // // states 表被普通鎖了 scope.Complete(); } #region Simple test [HttpPost("simple-test")] public async Task<IActionResult> SimpleTest() { using (var scope = SqlCommonMethod.CreateTransactionScope()) { await Inside(); scope.Complete(); // 這里依然不會解鎖, 要等到 scope 釋放 } // 直到 using 結束才會解鎖 table 哦 return Ok("ok"); }
總結 : 每次設置級別后最好是設置回去. 不然全場都會使用高級鎖.
ef core 有 unit of work 的概念,當我們 save change 時會自動使用 transaction 確保更新的一致性. 隔離級別是默認的 read committed 不允許臟讀.
但是呢, 有時候我們希望擁有更好的隔離級別, 比如 repeatable read, serializable
那么就需要調用 database.beginTransaction 了.
一旦需要自己控制 trans 麻煩就跟着來了。
比如在多個服務嵌套調用時, 如何共享 trans 呢 ?
每個服務的 trans 級別也有可能是不同的.
如果我們單純使用 beginTransaction 那么需要在每個服務寫判斷,是否有 current transaction 做出不同的處理.
在早期, 我們會用 transaction scope 作為業務級別的事務.
transaction scope 非常強大, 可以跨庫, 分布式, 甚至可以鏈接 file system
比如一個事務內做了數據庫修改,也創建了 file, 如果事務最終失敗,連 file 也可以 rollback 刪除掉.
但自從 ef 出現后, 這個就變得大材小用了些. ef 也不推薦我們使用了 refer https://docs.microsoft.com/zh-cn/ef/ef6/saving/transactions?redirectedfrom=MSDN
ef core 在 2.1 的時候支持了 transactionscope 但是不支持分布式, 好像是不兼容 linux 所以去掉了.
但是在我說的這種情況下,使用它依然會讓代碼變得更好.
調用起來是這樣的
using (var scope = new TransactionScope( scopeOption: TransactionScopeOption.Required, transactionOptions: new TransactionOptions { IsolationLevel = System.Transactions.IsolationLevel.RepeatableRead }, asyncFlowOption: TransactionScopeAsyncFlowOption.Enabled )) { try { } catch (Exception ex) { return BadRequest(ex.Message); } scope.Complete(); }
默認隔離級別是 serializable.
如果想在 service 里面嵌套, 那么重要設定 scopeOption 是 required 就可以了.
它還有另外 2 個選擇, 1 個是 new 意思是另外開啟一個獨立的 trans, 再一個是 suppend 就是完全沒有 trans 無關.
有了這些就很靈活了,在不同 service 中我們可以去實現獨立或無關的事務處理.
使用過程中需要注意幾件事情
嵌套 scope 需要使用同一種級別
這個挺麻煩的,通常不可能全部一個級別吧...
目前沒有看到方法可以修改的,一個可能的辦法是直接調用 sql 語句
set transaction isolation level read committed
set transaction isolation level repeatable read;
set transaction isolation level serializable
去設定它.
這里順便說說 sql server 對於這一塊的處理.
https://www.cnblogs.com/keatkeat/p/11830113.html
另一個要注意的是, 一定要設置 async enabled 如果 scope 內需要 async 的話
asyncFlowOption: TransactionScopeAsyncFlowOption.Enabled
refer :
https://www.cnblogs.com/csdbfans/p/transactionscope.html
https://blog.csdn.net/qin_yu_2010/article/details/86150247
https://docs.microsoft.com/en-us/ef/core/saving/transactions
https://www.cnblogs.com/taiyonghai/p/6047849.html
https://www.21cto.com/article/1075
https://www.codeproject.com/Articles/690136/All-About-TransactionScope#hBusinessTrans
https://codewala.net/2018/05/06/transactionscope-a-simple-way-to-handle-transactions-in-net/