TransactionScope即范圍事務(類似數據庫中的事務),保證事務聲明范圍內的一切數據修改操作狀態一致性,要麼全部成功,要麼全部失敗回滾.
MSDN:如果在事務范圍內未不發生任何異常 (即之間的初始化 TransactionScope 對象並調用其 Dispose 方法),則范圍所參與的事務可以繼續,否則參與到其中的事務將回滾。
當應用程序完成所有工作時它想要在事務中執行,應調用 Complete 方法一次,以通知該事務管理器是可接受(此時事務並未提交),即可提交事務,未能調用此方法中止事務。
調用 Dispose 方法將標記事務范圍的末尾。 在調用此方法之後所發生的異常不會影響事務。
假如現在有一個需求:實現一個下單功能,主要業務包含扣減商品庫存、扣減用戶賬戶余額、生成訂單記錄以及記錄日志。為了保證數據一致性我們通常的做法就是在數據庫建一個下訂單的事務,然後程序調用事務傳入相應的參數即可。那麼問題來了,如果用戶的賬戶數據跟訂單數據分別處於不同的數據庫,就沒法在同一個數據庫事務裡完成所有任務,也就沒法保證數據的一致性。
最近由於業務的變更公司改用MySQL數據庫,處理數據變更時習慣性先寫事務,寫的時候發現現有數據庫中一個事務都沒有,於是去問java組,不使用事務怎麼保證數據的一致性?得到的答復是:事務是什麼鬼,spring幫我們解決所有問題...。立馬就懵逼了,.net中沒聽說有Spring啊(據說有類似的框架),雖然可以考慮使用倉儲加工作單元來解決,但是感覺好麻煩的樣子,後來尋找解決方案時發現了TransactionScope。
1 try 2 { 3 using (TransactionScope scope = new TransactionScope()) 4 { 5 //TODO:數據處理業務 6 scope.Complete(); 7 } 8 } 9 catch (Exception ex) 10 { 11 throw ex; 12 }
1 //數據庫訪問對象 2 public class Studuent 3 { 4 public static IList<StudentModel> GetList() 5 { 6 //不允許髒讀 7 string sql = "SELECT Id,Name,CreateTime FROM Student ORDER BY Id DESC;"; 8 DataTable dt = SQLHelper.ExecuteDataTable(CommandType.Text, sql); 9 return dt.ToModel<StudentModel>(); 10 } 11 12 public static bool Add(string name) 13 { 14 SqlParameter[] parms = { new SqlParameter("@Name", SqlDbType.NVarChar, 32) { Value = name } }; 15 string sql = "INSERT INTO Student (Name) VALUES (@Name)"; 16 return SQLHelper.ExecuteNonQuery(CommandType.Text, sql, parms) > 0; 17 } 18 19 public static void Clear() 20 { 21 SQLHelper.ExecuteNonQuery("DELETE Student"); 22 } 23 }
1 //公共方法,輸出學生列表 2 static void PrintStudent() 3 { 4 IList<StudentModel> list = Studuent.GetList(); 5 foreach (var item in list) 6 { 7 Console.WriteLine("{0}\t{1}\t{2}", Thread.CurrentThread.ManagedThreadId, item.Name, item.CreateTime); 8 } 9 Console.WriteLine(); 10 }
最初我以為在執行Complete後即刻提交,但根據輸出結果可以看出,Complete方法執行兩秒之後事務依舊沒有提交。因為不允許髒讀的原因,主線程會在事務對Student表操作完成後才可查詢完成,但學生列表是在scope調用dispose方法之後輸出,MSDN介紹說dispose確定事務范圍末尾,因此猜測事務是在調用dispose時被提交。
對比兩幅圖可以看出,異常在Complete之後發生並不會影響事務的提交,事務未提交是因為發生異常導致Complete未被執行。之前看過一篇文章說,如果TransactionScope范圍中沒有調用Complete會導致程序異常,我想他肯定是開玩笑的...