TransactionScope事務處理方法介紹及.NETCore中的注意事項
作者:依樂祝
今天在寫CzarCms的UnitOfWork的使用使用到了這個TransactionScope事務,因此對它進行了相關資料的查閱並記錄如下,希望對大夥在.NET Core中使用有所幫助。
寫在前面
您是否曾嘗試使用C#程式碼來實現事務?通常,我們在SQL中一次執行多個Insert / Update語句的話可能就會使用到事務。事務遵循ACID(原子性,一致性,隔離性,永續性)規則,這樣所有的語句要麼全部執行成功要麼全部被取消並執行回滾操作。 而我們今天要講的TransactionScope則可以允許我們在應用程式級別實現這個過程。在某些情況下,您可能需要在同一個資料庫甚至多個資料庫(分散式事務)中執行不同的操作,或者由於某些其他約束,它無法在資料庫級別來完成,或者應用程式的開發人員對資料庫的接觸較少,那麼這時候TransactionScope將會讓你遊刃有餘。
什麼是TransactionScope呢?
TransactionScope作為System.Transactions的一部分被引入到.NET 2.0。同時SqlClient for .NET Core 從 2.1 及以上版本開始提供對System.Transactions的支援 。 它是一個類,它提供了一種簡單的方法,可以將一組操作作為事務的一部分來進行處理,而不必擔心場景背後的複雜性。如果某個操作在執行的過程中失敗的話,則整個事務將失敗並執行回滾操作,從而撤消已完成的所有操作。所有這些都將由框架處理,從而確保資料的一致性。
如何使用TransactionScope呢?
要使用它,您需要新增System.Transactions的引用,如果你使用的是.net core的話。這個引用被包含在netcoreapp2.2System.Transactions.Local.dll
中, 該引用是框架庫的一部分(通常預設情況下不會自動新增)。新增後,在我們想要使用它的地方新增名稱空間 System.Transactions即可。程式碼如下所示:
try
{
using (TransactionScope scope = new TransactionScope())
{
// Do Operation 1
// Do Operation 2
//...
// 如果所有的操作都執行成功,則Complete()會被呼叫來提交事務
// 如果發生異常,則不會呼叫它並回滾事務
scope.Complete();
}
}
catch (ThreadAbortException ex)
{
// 處理異常
}
在上面的程式碼中我們可以看到我們在建立TransactionScope例項時使用了using
語句塊及Disposable塊,它確保了當dispose離開塊並結束事務範圍時呼叫dispose來進行資源的釋放。
在一個Transaction範圍中,我們可以做多個連線甚至連結到不同資料庫的操作的,如下所示:
using (TransactionScope scope = new TransactionScope())
{
using (con = new SqlConnection(conString1))
{
con.Open();
// 執行操作 1
// 執行操作 2
//...
}
using (con = new SqlConnection(conString2))
{
con.Open();
// 執行操作 1
// 執行操作 2
//...
}
scope.Complete();
}
下面我們使用兩個不同的資料庫連線字串來連線不同的資料庫。當然我們也可以根據我們的業務要求使用盡可能多資料庫。我們也可以再事務中巢狀事務。如下程式碼所示:
public void DoMultipleTransaction()
{
try
{
using (TransactionScope scope = new TransactionScope())
{
using (con = new SqlConnection(conString1))
{
con.Open();
// 執行操作1
}
OtherTransaction();
scope.Complete();
}
}
catch (ThreadAbortException ex)
{
// 處理異常
}
}
private void OtherTransaction()
{
using (TransactionScope scope = new TransactionScope())
{
using (con = new SqlConnection(conString2))
{
con.Open();
// 執行操作
}
scope.Complete();
}
}
這裡最頂層的事務範圍稱為根範圍。另外這裡需要注意的是即使通過呼叫scope.Complete()完成內部事務(上面的OtherTransaction ),如果由於各種原因無法呼叫rootscope complete,那麼整個事務也將被回滾包括內部的事務。
*注意:執行分散式trsanctions時,您可能會收到以下異常之一*
- 伺服器上的MSDTC不可用
- 已禁用分散式事務管理器(MSDTC)的網路訪問。
這兩個錯誤都是由於同樣的原因,第一個是在資料庫和應用程式是同一個伺服器時發生的,而在另一個則是服務跟資料庫分別部署在兩臺伺服器上。對於同一臺伺服器,請轉到run-> cmd-> services.msc。執行名為Distributed Transaction Coordinator的服務並自動啟動啟動型別,以便在系統重新啟動時再次啟動它。對於2,你可能需要參照這個連結的內容進行相應的設定
TransactionScope 類提供了多個過載建構函式,它們接受 TransactionScopeOption 型別的列舉,而該列舉定義事務範圍行為。
TransactionScope物件有以下三個選項:
- Required:聯接環境事務,或者在環境事務不存在的情況下建立新的環境事務。
- RequiresNew:成為新的根範圍,也就是說,啟動一個新事務並使該事務成為其自己範圍中的新環境事務。
- Suppress:根本不參與事務。 因此沒有環境事務。
如果用 Required] 例項化範圍並且存在環境事務,則該範圍會聯接該事務。 相反,如果不存在環境事務,該範圍就會建立新的事務併成為根範圍。 這是預設值。 在使用 Required時,無論範圍是根範圍還是僅聯接環境事務,該範圍中的程式碼都不需要有不同的行為。 該程式碼在這兩種情況下的行為應相同。
如果用 RequiresNew 例項化範圍,則它始終為根範圍。 它會啟動一個新事務,並且其事務成為該範圍中的新環境事務。
如果用 Suppress 例項化範圍,則無論是否存在環境事務,範圍都從不參與事務。 始終使用此值例項化的作用域具有null作為其環境事務。
下面來讓我們看一組例項程式碼:
using (TransactionScope scope = new TransactionScope())
{
// 聯接環境事務,或者在環境事務不存在的情況下建立新的環境事務。
using (TransactionScope scope1 = new TransactionScope(TransactionScopeOption.Required))
{
// Do Operation
scope1.Complete();
}
//成為新的根範圍,也就是說,啟動一個新事務並使該事務成為其自己範圍中的新環境事務。
using (TransactionScope scope2 = new TransactionScope(TransactionScopeOption.RequiresNew))
{
// Do Operation
scope2.Complete();
}
//根本不參與事務。 因此沒有環境事務。
using (TransactionScope scope3 = new TransactionScope(TransactionScopeOption.Suppress))
{
// Do Operation
scope3.Complete();
}
scope.Complet
在這裡,我們使用不同的TransactionScopeOptions在父事務下建立了三個事務。預設情況下,範圍是required ,這裡父事務就是採用的這個預設引數進行建立的。它是一個建立新事務的根範圍,並將其標記為環境事務。scope1也是使用required建立的,因為我們已經有了一個環境事務(範圍),所以它加入到父事務中。scope2是使用RequiresNew選項建立的,這意味著它是一個獨立於環境事務處理的新事務。scope3是用suppress建立的選項,這意味著它不參與任何環境事務。無論環境事務是否成功執行,它都會被執行。父(全域性)範圍完成後,將提交所有環境事務。
注意點
- EF Core 依賴資料庫提供程式以實現對 System.Transactions 的支援。 雖然支援在 .NET Framework 的 ADO.NET 提供程式之間十分常見,但最近才將 API 新增到 .NET Core,因此支援並未得到廣泛應用。 如果提供程式未實現對 System.Transactions 的支援,則可能會完全忽略對這些 API 的呼叫。 SqlClient for .NET Core 從 2.1 及以上版本開始支援 System.Transactions。如果嘗試在低版本中 如.NET Core 2.0中嘗試使用該功能將引發異常。
- 自版本 2.1 起,.NET Core 中的 System.Transactions 實現將不包括對分散式事務的支援,因此不能使用
TransactionScope
或CommittableTransaction
來跨多個資源管理器協調事務。主要是不依賴windows中的mstsc功能。 - 非同步方法使用時需要注意:
在下面的例子中,我們在TransactionScope
內部使用await
。
using(var scope = new TransactionScope())
{
var groups = await Context.ProductGroups.ToListAsync()。ConfigureAwait(false);
}
看起來沒有問題,但它會丟擲一個 System.InvalidOperationException:
`A TransactionScope must be disposed on the same thread that it was created.`
原因是預設情況下TransactionScope
不會從一個執行緒切換到另一個執行緒。為了解決這個問題,我們必須使用 TransactionScopeAsyncFlowOption.Enabled
:
using(var scope = new TransactionScope(TransactionScopeAsyncFlowOption.Enabled))
{
var groups = await Context.ProductGroups.ToListAsync()。ConfigureAwait(false);
}
現在應該可以了吧?這取決於下面的情況。
如果我們使用和不使用TransactionScopeAsyncFlowOption這個
選項的時候都使用了相同的資料庫連線,並且第一次執行的時候沒有使用這個選項,那麼我們會得到另一個異常: System.InvalidOperationException:
`Connection currently has transaction enlisted. Finish current transaction and retry.`
換句話說,由於第一個訪問的原因,第二個會話將會失敗。如下程式碼所示:
try
{
using (var scope = new TransactionScope())
{
// We know this one - System.InvalidOperationException:
// TransactionScope必須放在與建立它相同的執行緒上。
var groups = await Context.ProductGroups.ToListAsync().ConfigureAwait(false);
}
}
catch (Exception e)
{
// error handling
}
using (var scope = new TransactionScope(TransactionScopeAsyncFlowOption.Enabled))
{
// Implemented correctly but throws anyways
// System.InvalidOperationException:
// 當前連線已經被記錄。完成當前事務並重試。
var groups = await Context.ProductGroups.ToListAsync().ConfigureAwait(false);
}
想象一下,如果第一個呼叫是在第三方庫或您正在使用的框架中完成的,二您不瞭解其中的程式碼 – 如果您之前沒有看到此錯誤,那麼你講無從下手來解決這個問題。
## 總結
本文帶著大家熟悉了一遍TransactionScope並對其使用進行了介紹!同時介紹了在.NET Core中使用TransactionScope的一些注意事項!希望對大家有所幫助。
相關文章
- 4.Rxjs介紹及注意事項JS
- netcore後臺任務注意事項NetCore
- Oracle vs PostgreSQL,研發注意事項(6)- 事務處理OracleSQL
- 選擇代理ip注意事項介紹
- Storm介紹&實際開發注意事項ORM
- 事務的介紹
- 開發及上線中的注意事項
- ERP選型準備、方法及注意事項
- 【DATAGUARD】Oracle21c Dataguard建立注意事項及主要引數介紹Oracle
- react-webcomponentify元件介紹、原碼分析、注意事項ReactWeb元件
- Python eval的用法及注意事項Python
- Guava HashMultimap使用及注意事項Guava
- 搭建 nuget 私服及注意事項
- 分散式事務處理方案,微服事務處理方案分散式
- Web前端面試自我介紹對話技巧注意事項Web前端面試
- 關於mysqldump備份非事務表的注意事項MySql
- cookie的使用方法以及注意事項Cookie
- 介面開發文件及注意事項
- 微服務熔斷隔離機制及注意事項微服務
- mysqli 事務處理MySql
- springboot事務處理Spring Boot
- MySQL事務處理MySql
- MySQL的事務處理及隔離級別MySql
- 分散式事務介紹分散式
- .Net上傳檔案處理三大正規化,及開發注意事項
- Linux中單引號和雙引號的使用方法及注意事項!Linux
- C中memcpy使用注意事項memcpy
- @Lombok注意事項Lombok
- RandomAccessFile注意事項randomMac
- TDengine|taosdump 的使用方法和注意事項
- TDengine | taosdump 的使用方法和注意事項
- oracle分散式事務異常處理方法Oracle分散式
- Go 中修改切片副本的注意事項Go
- 自學中應該注意的事項
- 如何搭建伺服器及注意事項伺服器
- 段合併優化及注意事項優化
- SVN安裝配置及安全注意事項
- Oracle使用*的注意事項Oracle