由於找不到更好的方法,項目也沒采用任何的第三方框架(如NHibernate),所以在業務層的事務處理部分我一直用TransactionScope這個東東來管理業務事務,微軟把它翻譯為范圍事務,用它可以實現范圍內的隱式事務,具體的使用方法本文就不多講了,可以參看MSDN:http://msdn.microsoft.com/zh-cn/library/ms172152.aspx
今天我遇到一個問題,在方法A中引入TransactionScope,A中調用方法B,B中也引入了TransactionScope(TransactionScope是可以嵌套的哦),我的代碼好多都是這麼寫的:
Code
1public OperationResult A()
2{
3 OperationResult result = new OperationResult();
4 int errorCount = 0;
5
6 using (TransactionScope tx = new TransactionScope())
7 {
8 try
9 {
10 //TODO
11 }
12 catch
13 {
14 errorCount++;
15 }
16 if (B().Result != ResultInfo.操作成功)
17 {
18 errorCount++;
19 }
20 if (C().Result != ResultInfo.操作成功) //如果B此時失敗了,調用C中的TransactionScope會出現“事務已終止”異常
21 {
22 errorCount++;
23 }
24 if (errorCount == 0)
25 {
26 result.Result = ResultInfo.操作成功;
27 tx.Complete();//我把complete全寫在了這裡
28 }
29 }
30
31 return result;
32}
33
34public OperationResult B()
35{
36 OperationResult result = new OperationResult();
37
38 using (TransactionScope tx = new TransactionScope())
39 {
40 try
41 {
42 //TODO
43 tx.Complete();
44 }
45 catch
46 {
47 result.Result = ResultInfo.操作失敗; //現在假設catch了,此時B中的TransactionScope並沒有調用complete
48 }
49 }
50}
51
52public OperationResult C()
53{
54 OperationResult result = new OperationResult();
55
56 //當B方法失敗時,也就是B沒執行complete時,調用到這裡會出現異常“事務已終止”
57 using (TransactionScope tx = new TransactionScope())
58 {
59 //
60 }
61}
這樣如果B中有方法執行不正確,就沒有執行complete,這樣,在被調用函數內部,直接終止了該環境事務,如果下面的代碼中還有引用TransactionScope的,就會拋出“事務已終止”的異常。
我一直以為complete應該放在“正確的業務邏輯”裡面,如果程序執行不正確就不要執行complete,它會自動回滾。我現在知道我的想法是灰常錯誤的,它是會回滾,它直接終止了都。以下是MSDN的解釋:
當您對范圍中的所有操作都已成功完成感到滿意時,應該僅調用此方法一次,以通知事務管理器所有資源上的狀態都是一致的,並且可以提交該事務。將該調用作為 using 塊中的最後一個語句是很好的做法。
有關如何使用此方法的更多信息,請參見 使用事務范圍實現隱式事務 主題。
未能調用此方法將中止事務,因為事務管理器將此解釋為系統故障或在事務范圍中引發了異常。但是還應該注意,調用此方法並不保證事務的提交。它只是一種將狀態通知給事務管理器的方式。在調用此方法之後,就不能再通過 Current 屬性訪問環境事務,嘗試這樣做將導致引發異常。
如果 TransactionScope 對象創建事務,則資源管理器之間的實際提交工作發生在 End Using 語句處。如果它未創建事務,則在每當 CommittableTransaction 對象的所有者調用 Commit 時發生提交。屆時事務管理器將調用資源管理器,並根據是否在 TransactionScope 對象上調用了此方法來通知它們進行提交或回滾。
看這句“未能調用此方法將中止事務”,這也就是說,如果你不調用complete方法,事務直接終止,“因為事務管理器將此解釋為系統故障或在事務范圍中引發了異常”,所以現在我理解透徹了TransactionScope,它能保證你在它的生命周期內的隱式事務,無論你做什麼數據庫操作,它都會為你把這些操作全組織到一個環境事務中,如果你哪個環節出現問題,整個事務會隱式的回滾,complete不是告訴事務管理器馬上開始提交,而只是通知事務管理器我的操作狀態完成了,我以前把它當dbtransaction了,執行成功才submit,不成功rollback,而complete只是說“我的活干完了,你可以提交了”,如果你不在它的生命周期內調用這個方法,事務會中止,此時就像我上面寫的方法B,當你再次嘗試(using一個new TransactionScope)的時候,你會碰到“事務已終止”的錯誤。所以,我以後的代碼修改成這樣:
Code
1public OperationResult A()
2{
3 OperationResult result = new OperationResult();
4 //int errorCount = 0;這個標識變量可以扔了
5
6 using (TransactionScope tx = new TransactionScope())
7 {
8 try
9 {
10 //TODO
11 }
12 catch
13 {
14 return result;
15 }
16 if (B().Result != ResultInfo.操作成功)
17 {
18 return result;
19 }
20 if (C().Result != ResultInfo.操作成功) //如果B此時失敗了,調用C中的TransactionScope會出現“事務已終止”異常
21 {
22 return result;
23 }
24 result.Result = ResultInfo.操作成功;
25 tx.Complete();//現在全放在TransactionScope的生命周期末尾
26 }
27
28 return result;
29}
30
31public OperationResult B()
32{
33 OperationResult result = new OperationResult();