關於 DB 操作 通常都會包成一個專用的類別 一來避免重複程式碼 二來分層結構降低耦合
所以我有個 accessDB.NonQuery() 方法 只要 傳入 SQL 指令 跟 SqlParameter 參數 即可完成 DB 操作 其他的細節被封裝在方法之中
但是你知道的 有些人會這樣搞 例如這樣 猜猜會怎樣?
毫不意外 碰! Exception 被顯示在 Label1 上
DB... WTF...
看一下原始碼 就知道為何了
先前的寫法 早就預設所有的方法都是一次性的叫用 也就是各自為一段獨立的 DB 操作
但是實際使用上通常都是先 INSERT 一下 SELECT 一下 運算一下 再 INSERT 一下
而搞完這一輪回 才算一次成功的 "交易"
顯而易見 我需要給每個 SqlCommand 包上 Transaction 如果要期望 呼叫的人把 Transaction 寫在 T-SQL 中 那我就太傻太天真了
但是我立馬遇到了挑戰...
1. SqlTransaction 只存活在 SqlConnection 一次的開關過程之中!! 詳洽 MSDN
2. 這是一個 靜態類別!! 我沒有辦法 設一個變數(靜態類別內只能有靜態變數)來記住 SqlConnection 或是 SqlTransaction 這樣會使得同時叫用的時候發生衝突
就算真的有辦法釋出 SqlConnection 或是 SqlTransaction 的話 另外的問題則是其開關變得無法控制
當初設計這個類別 就是為了封裝 DB 操作 防止外部變因介入 這麼做等於違背初衷
沒錯 我必須在 「不提對外提供 SqlConnection 或是 SqlTransaction」的前提下 完成 Transaction 的封裝
如果是從前的我鐵定會說沒計 不然就是改成一般類別 用 new 物件的方式 獨立執行個體 則沒有衝突的問題
但是現在的我不一樣了!! 因為未來的我坐著時光機到了現在告訴了我解答!! (P.S 據未來的我表示 時光機是跟某藍色生物借的)
答案就是 我可以使用強大的委派!!
首先 封裝 建立 SqlConnection 和 SqlTransaction 成一個私有方法 並完整的包覆整個開關和交易流程 唯有叫用這個方法 才會執行完整的 DB 操作
公開方法 UsingTransactionActions 方法 是一個接受型別為 Action
在該方法之中 封裝了實際取的 SqlConnection 跟 SqlTransaction 的實作 也就是透過呼叫私有方法 UsingConnection
在外部使用上 不需要知道 傳入參數由何而來 (也不允許被知道)
另外將原先的 accessDB.NonQuery() 方法增加一個多載 傳入 SqlConnection 跟 SqlTransaction
請注意 唯有使用 UsingTransactionActions 方法 才有辦法在其參數(委派方法)中得到 SqlConnection 跟 SqlTransaction
UsingTransactionActions 使用方法
打完收工