iT邦幫忙

DAY 25
3

Kuick Application & ORM Framework系列 第 25

Kuick -- 資料庫 Transaction (一)

需求情境:
要如何為已經包在其他方法裡的資料處理加上 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


上一篇
Kuick -- Entity 實作支援 dynamic
下一篇
Kuick -- 資料庫 Transaction (二)
系列文
Kuick Application & ORM Framework34

2 則留言

我要留言

立即登入留言