可彈性支援Transaction的EF資料異動方法
0 |
考慮以下DataHelper靜態型別,有AddWorkItem及AddDataGroup兩個方法透過Entity Framework新増資料:
public class DataHelper
{
//使用統一的靜態函式建立DbContext物件,避免自行建構
public static BBDPEntities CreateDbContext()
{
//正式應用時,設定檔之連線字串應加密
//在此進行讀取設定並解密以建構DbContext,細節待日後介紹
return new BBDPEntities();
}
public static void AddWorkItem(Guid wuid, string subject)
{
using (var ctx = CreateDbContext())
{
var wi = new WorkItem()
{
Wuid = wuid,
Subject = subject
};
ctx.WorkItem.Add(wi);
ctx.SaveChanges();
}
}
public static void AddDataGroup(string costCenter, string jobType)
{
using (var ctx = CreateDbContext())
{
var dg = new DataGroup()
{
CostCenter = costCenter,
JobType = jobType
};
ctx.DataGroup.Add(dg);
ctx.SaveChanges();
}
}
}
在兩個方法裡各自產生DbContext物件(實務上多使用統一的靜態方法,不要自行用new建構,以集中連線字串解密以及其他初始化邏輯),建立Entities物件,使用Add()加入,呼叫SaveChanges()存入。如果我們想將AddWorkItem()及AddDataGroup()兩個新増動作包成交易(Transaction)要怎麼做?
跟LINQ to SQL一樣,要將EF的多個資料庫操作包成交易也有三種選擇:
- Implicit Transaction:
使用同一個DbContext進行新增/更新/刪除等動作,SaveChanges()時EF會自動將資料庫異動包成交易,這是最單純最有效率的做法。 - Explicit Local Transaction:
使用DbContext.Database.BeginTransaction()取得DbContextTransaction物件,自行呼叫Commit()、Rollback()控制交易成立或回滾。這個做法還有一個好處-透過SqlQuery()及ExecuteSqlCommand()直接執行的指令也能納入交易範圍。 - Explicit Distributable Transaction:
使用TransactionScope將資料庫動作包起來,範圍內的資料庫操作都會參與交易。Transaction的彈性最大,甚至能支援異種資料庫的分散式交易,但要注意包在TransactionScope中的資料庫動作會啟用LTM或OleTx,成本較為昂貴(LTM較輕巧,但有些限制,如果涉及一台以上DB或對遠端SQL開了兩條不同連線,就一定要用貴森森的OleTx),必要時要將不必參與Transaction的部分隔離開以增進效能。(延伸閱讀: .NET分散式交易程式開發FAQ )
TransactionScope做法最無腦最好寫,將資料異動方法當成黑盒子用TransactionScope包起來就搞定。
public static void Test()
{
using (var tx = new TransactionScope())
{
DataHelper.AddWorkItem(Guid.NewGuid(), "SUBJECT");
DataHelper.AddDataGroup("12345", "A");
}
}
TransactoinScope固然方便,但不管走LTM或OleTx,效能都不如在同一條資料庫連線進行的Implicit Transaction。實務上我偏好的做法是由Test()建立DbContext,讓AddWorkItem與AddDataGroup共用一個DbContext,AddWorkItem及AddDataGroup新増資料後不執行SaveChanges(),待全部動作完成再由Test()呼叫SaveChanges()。
如此,AddWorkItem跟AddDataGroup需增加一個參數接收外部傳入的DbContext。為了方便使用,我常用一個小技巧讓AddWorkItem跟AddDataGroup變聰明,遇到外部傳入DbContext就共用,外部沒有傳入時自己生一個。如以下範例:
public static void Test()
{
using (var ctx = DataHelper.CreateDbContext())
{
DataHelper.AddWorkItem(Guid.NewGuid(), "SUBJECT", ctx);
DataHelper.AddDataGroup("12345", "A", ctx);
}
}
//...略...
public class DataHelper
{
//使用統一的靜態函式建立DbContext物件,避免自行建構
public static BBDPEntities CreateDbContext()
{
//正式應用時,設定檔之連線字串應加密
//在此進行讀取設定並解密以建構DbContext,細節日後介紹
return new BBDPEntities();
}
public static void AddWorkItem(Guid wuid, string subject,
BBDPEntities exCtx = null)
{
var ctx = exCtx ?? CreateDbContext())
ctx.Database.BeginTransaction();
var wi = new WorkItem()
{
Wuid = wuid,
Subject = subject
};
ctx.WorkItem.Add(wi);
if (exCtx == null)
{
ctx.SaveChanges();
ctx.Dispose();
}
}
public static void AddDataGroup(string costCenter, string jobType,
BBDPEntities exCtx = null)
{
var ctx = exCtx ?? CreateDbContext())
var dg = new DataGroup()
{
CostCenter = costCenter,
JobType = jobType
};
ctx.DataGroup.Add(dg);
if (exCtx == null)
{
ctx.SaveChanges();
ctx.Dispose();
}
}
}
以上是我處理EF交易常用的小技巧,分享大家參考。
Comments
Be the first to post a comment