在資料庫工具類編寫的過程中,對事務的處理操作想避免各個原子操作的事務對象賦值重覆操作,想對外暴露的方法為如下形式 public bool ExecuteTransition(Action TransitionAction, out string ExceptionStr) 外部傳入的資料庫操作都使用 ...
在資料庫工具類編寫的過程中,對事務的處理操作想避免各個原子操作的事務對象賦值重覆操作,想對外暴露的方法為如下形式
public bool ExecuteTransition(Action TransitionAction, out string ExceptionStr)
外部傳入的資料庫操作都使用委托統一打包,內部進行事務操作。我們首先需要明白的是,資料庫事務操作在ADO.NET的編碼中的體現是,DbConnection為同一個,DbCommand的Transaction為同一個。
首先我們需要識每一個資料庫操作的上下文,是否在TransitionAction這個委托中,為了簡單明瞭,在執行TransitionAction時開啟一個Task,取得當前線程的ThreadID作為這個事務委托的唯一標識,並生成一個DbTransaction放入一個TransactionDic中,在SqlHelper執行類中執行SQL語句創建Connection時,取得當前的ThreadID去TransactionDic中查找,如果有對應的Transition則說明該SQL語句的執行是在一個事務中,Connection直接取Transition的資料庫連接,並給DbCommand的Transition對象賦值
這個解決方案對於TransitionAction中執行方法類中的資料庫操作或其他組合操作也是可行的,但是對於嵌套事務還需要進一步改進。
比如我封裝好一個框架的工作流方法MethodA,自帶事物執行,但是需要與業務更新方法MethodB進行事物組合操作,上述方案並不能滿足要求,需要我們進行改進,判斷當前的事物TransitionAction是否是嵌套事務,即TransitionActionB實際是打包在TransitionActionA中的。在TransitionAction的DbTransaction添加的過程中,我們需要取到Task之外的ThreadID,這裡稱作為RootThreadID,同時維護一個ConcurrentDictionary<string, List<string>> TransitionIDMapDic,用於維護RootThreadID與嵌套事務的ThreadID的關係,在創建Task時就可以判斷當前的ThreadID是否在TransactionDic,存在就是嵌套事務,需要將當前的TransitionAction合併到Root事物中
TransactionManage 代碼
public class TransactionManage { private static ConcurrentDictionary<string, LocalTransaction> TransactionDic = new ConcurrentDictionary<string, LocalTransaction>(); private static ConcurrentDictionary<string, List<string>> TransitionIDMapDic = new ConcurrentDictionary<string, List<string>>(); public static void AddTransition(string TransitionID,string RootThreadID, DbTransaction Transition,Action TransitionAction) { LocalTransaction LT = new LocalTransaction(); LT.RootThreadID = RootThreadID; LT.TransitionID = TransitionID; LT.Transition = Transition; //執行列表增加Action LT.AddTransitionAction(TransitionAction); TransactionDic.TryAdd(TransitionID, LT); //增加事務根線程ID與嵌套事務相關事務ID TransitionIDMapDic.TryAdd(RootThreadID, new List<string>() { TransitionID }); } public static void ContactTransition(string TransitionID, string RootThreadID,Action TransitionAction) { LocalTransaction LT = TransactionDic[RootThreadID]; if (!TransactionDic.ContainsKey(LT.RootThreadID)) { LT.TransitionID = TransitionID; //執行列表增加Action LT.AddTransitionAction(TransitionAction); TransactionDic.TryAdd(TransitionID, LT); //增加事務根線程ID與嵌套事務相關事務ID List<string> TransitionIDS = TransitionIDMapDic[LT.RootThreadID]; TransitionIDS.Add(TransitionID); TransitionIDMapDic[LT.RootThreadID] = TransitionIDS; } else { ContactTransition(TransitionID, LT.RootThreadID, TransitionAction); } } public static string GetRootID(string TransitionID) { LocalTransaction LT = TransactionDic[TransitionID]; if (!TransactionDic.ContainsKey(LT.RootThreadID)) { return LT.RootThreadID; } else { return GetRootID(LT.RootThreadID); } } public static LocalTransaction GetTransition(string TransitionID) { LocalTransaction LT = null; TransactionDic.TryGetValue(TransitionID, out LT); return LT; } public static bool ContainsTransition(string TransitionID) { return TransactionDic.ContainsKey(TransitionID); } public static void RemoveTransition(string TransitionID) { string RootID = GetRootID(TransitionID); List<string> TransitionIDList = null; TransitionIDMapDic.TryRemove(RootID, out TransitionIDList); foreach (string TransitionIDItem in TransitionIDList) { LocalTransaction LT = null; TransactionDic.TryRemove(TransitionIDItem, out LT); } }
對外事物執行方法
public bool ExecuteTransition(Action TransitionAction, out string ExceptionStr) { bool IsSuccess = true; ExceptionStr = string.Empty; string RootThreadID = Thread.CurrentThread.ManagedThreadId.ToString(); var TrabsitionTask = new Task<LocalTransactionResult>(() => { string TransitionID = Thread.CurrentThread.ManagedThreadId.ToString(); LocalTransactionResult Result = new LocalTransactionResult(); if (!TransactionManage.ContainsTransition(RootThreadID)) { using (DbConnection connection = DBExecute.CreateConnection(ConnectionString)) { connection.Open(); DbTransaction Transaction = connection.BeginTransaction(); TransactionManage.AddTransition(TransitionID, RootThreadID, Transaction, TransitionAction); try { TransactionManage.GetTransition(TransitionID).Execute(); Transaction.Commit(); } catch (System.Exception e) { Result.ExecuteStatus = false; Result.ExceptionMessage = e.Message; Transaction.Rollback(); } finally { Transaction.Dispose(); connection.Close(); connection.Dispose(); Transaction = null; TransactionManage.RemoveTransition(TransitionID); } return Result; } } else { //當前是嵌套事務,不執行,由根事務統一執行 TransactionManage.ContactTransition(TransitionID, RootThreadID, TransitionAction); Result.ExecuteStatus = true; Result.ExceptionMessage = string.Empty; return Result; } }); TrabsitionTask.Start(); TrabsitionTask.Wait(); IsSuccess = TrabsitionTask.Result.ExecuteStatus; ExceptionStr = TrabsitionTask.Result.ExceptionMessage; return IsSuccess; }
完整模塊代碼地址:https://gitee.com/grassprogramming/FastExecutorCore/tree/master/code/FastCore/FastCore/FastORM
註:個人感覺使用線程的方式雖然很方便但是實際使用過程中多線程可能會出現問題,後續會對執行類進行上下文對象的綁定改造