需求情境:
要如何為已經包在其他方法裡的資料處理加上 Transaction?
如果是改寫原有的方法,其他呼叫這些方法的程式是否會受影響?
如果是複製方法裡的商業邏輯,包成有 Transaction 的處理,造成需要維護多份相同的程式,這樣好嗎?
答:
使用 Kuick 處理資料,您可以不管是否使用 Transaction,只要在有需要的地方,以 TransactionApi 將呼叫其他方法包起來即可。
先簡單在下面說明 Kuick 是如何處理的,本文內再細講原本 ADO.NET 的處理方式與 Kuick 的差異。
原有 2 個各自進行資料處理的方法:
沒有交易,呼叫方法進行資料處理:
啟用交易,呼叫方法進行資料處理:
<舉例說明 ADO.NET 對於 Transaction 的作法>
原有 2 個各自進行資料處理的方法:
// 新增
private void InsertData()
{
using(SqlConnection con = new SqlConnection(connectionString)) {
con.Open();
using(SqlCommand cmd = con.CreateCommand()) {
cmd.CommandText = "Insert ...";
cmd.ExecuteNonQuery();
}
}
}
// 刪除
private void DeleteData()
{
using(SqlConnection con = new SqlConnection(connectionString)) {
con.Open();
using(SqlCommand cmd = con.CreateCommand()) {
cmd.CommandText = "Delete ...";
cmd.ExecuteNonQuery();
}
}
}
沒有交易,呼叫方法進行資料處理:
// 沒有交易
// 呼叫 InsertDate, DeleteData 方法
private void ExecuteNonTransaction()
{
InsertData();
DeleteData();
}
原本很單純,但是在需要將多個資料處理包在同一個 Transaction 裡的時候,問題來了。
解決方法一、改寫原有的方法
新方法支援傳入 SqlCommand 物件,使用共同的 SqlCommand 物件呼叫 2 個新方法。
// 新增
private void InsertData(SqlCommand cmd)
{
cmd.CommandText = "Insert ...";
cmd.ExecuteNonQuery();
}
// 刪除
private void DeleteData(SqlCommand cmd)
{
cmd.CommandText = "Delete ...";
cmd.ExecuteNonQuery();
}
// 啟用交易
// 呼叫新的 InsertDate, DeleteData 方法,包在同一個 Transaction 裡
private void ExecuteWithTransaction()
{
using(SqlConnection con = new SqlConnection(connectionString)) {
con.Open();
SqlTransaction tra = con.BeginTransaction(IsolationLevel.ReadCommitted);
using(SqlCommand cmd = con.CreateCommand()) {
cmd.Transaction = tra;
try {
// 新增:呼叫 InsertDate 方法
InsertData(cmd);
// 刪除:呼叫 UpdateDate 方法
DeleteData(cmd);
// Commit
tra.Commit();
} catch {
// Rollback
tra.Rollback();
}
}
}
}
以多載 (overload) 為原有的方法提供支援交易處理,除了花費額外的開發時間之外,這讓專案程式碼更加複雜,更難維護。
解決方法二、複製方法裡的商業邏輯
複製原方法裡的商業邏輯,並以啟用 Transaction 的處理
// 啟用交易
// 複製原方法裡的商業邏輯
private void ExecuteWithTransaction()
{
using(SqlConnection con = new SqlConnection(connectionString)) {
con.Open();
SqlTransaction tra = con.BeginTransaction(IsolationLevel.ReadCommitted);
using(SqlCommand cmd = con.CreateCommand()) {
cmd.Transaction = tra;
try {
// 新增:將 InsertDate 方法的商業邏輯複製一份
cmd.CommandText = "Insert ...";
cmd.ExecuteNonQuery();
// 刪除:將 DeleteData 方法的商業邏輯複製一份
cmd.CommandText = "Delete ...";
cmd.ExecuteNonQuery();
// Commit
tra.Commit();
} catch {
// Rollback
tra.Rollback();
}
}
}
}
這個解決方式違反軟體開發的不自我重複原則 (Don't repeat yourself)
<舉例說明 Kuick 對於 Transaction 的作法>
原有 2 個各自進行資料處理的方法:
// 新增
public void InsertData()
{
UserEntity user = new UserEntity();
user.UserName = "kevinjong";
user.Add();
}
// 刪除
public void DeleteData()
{
UserEntity
.Sql()
.Where(x => x.Actived == false)
.Remove();
}
沒有交易,呼叫方法進行資料處理:
// 沒有交易
// 呼叫 InsertDate, DeleteData 方法
private void ExecuteNonTransaction()
{
InsertData(); // 新增:呼叫 InsertDate 方法
DeleteData(); // 修改:呼叫 UpdateDate 方法
}
啟用交易,呼叫方法進行資料處理:
// 啟用交易
// 只要使用 TransactionApi
// 將呼叫 InsertDate, DeleteData 方法包起來即可
private void ExecuteWithTransaction()
{
using(TransactionApi api = new TransactionApi()) {
try {
InsertData(); // 新增:呼叫 InsertDate 方法
DeleteData(); // 修改:呼叫 UpdateDate 方法
api.Commit(); // 成功 Commit
} catch {
api.Rollback(); // 失敗 Rollback
}
}
}
========================================
您能感受兩者之間的差異嗎?
下一篇分享『Kuick -- 資料庫 Transaction (二)』將說明 Kuick 如何實作 Transaction。
========================================
鐵人賽分享列表:Kuick Application & ORM Framework
開放原始碼專案:kuick.codeplex.com
直接下載原始碼:Kuick
下載相關文件檔:C# Code Conventions and Design Guideline
相關教學影片區:Kuick on YouTube