iT邦幫忙

DAY 26
2

Kuick Application & ORM Framework系列 第 26

Kuick -- 資料庫 Transaction (二)

Kuick 重視設計期的便利與直覺,尤其是在資料存取方法上,今日就請您看看幕後是如何達成。

預告下一篇分享『Kuick -- 遮斷器 Interceptor』。
請先參考 Wikipedia: Interceptor pattern
本篇分享部份功能包含於下一版本裡,預計 2012-11-10 前發布於 <kuick.codeplex.com>

Kuick 以 Entity 為設計中心,集中商業邏輯,直接跟資料庫溝通。在停用 Transaction 時,可以全然乎略背後如何跟資料庫溝通,在啟用 Transaction 時,只需建立 TransactionApi 物件包覆處理區段,即使是呼叫其他方法也可達成。

<相關類別>
首先列出相關類別:

  1. Kuick.Data.Api
  2. Kuick.Data.TransactionApi
  3. Kuick.Data.Entity
  4. Kuick.Data.Entity<T>
  5. Kuick.Data.Sql
  6. Kuick.Data.Sql<T>

<何種操作需要使用 Transaction>
資料『新增』、『修改』、『刪除』操作時,依據需求啟用 Transaction,『選取』操作一律停用 Transaction。

<Api & TransactionApi>
TransactionApi 是 Api 的衍生類別 (Derived Class),僅額外處理 Transaction 相關內容。
停用 Transaction 的資料操作,是透過建立各自的 Api 物件完成後就關閉。
啟用 Transaction 的資料操作,是透過建立共用的 TransactionApi 物件,整個交易區段完成後再關閉

<交易區段>
這有 2 個問題需要解決:

1. 如何判斷目前在同一個交易區段內
以下程式碼摘錄自 Kuick.Data.TransactionApi.ScopeId property (包含在下一版本更新)

public static string ScopeId
{
	get
	{
		try {
			if(Current.IsWebApplication) {
				if(null == HttpContext.Current.Session) { 
					return Utility.GetUuid(); 
				}
				return string.Concat(
					HttpContext.Current.Session.SessionID,
					":",
					HttpContext.Current.Timestamp.Ticks.ToString()
				);
			} else {
				return Thread.CurrentThread.ManagedThreadId.ToString();
			}
		} catch(Exception ex) {
			Logger.Error(
				"TransactionApi.ScopeId",
				ex.ToAny()
			);
			return Utility.GetUuid();
		}
	}
}

為了達成呼叫其他方法也可包覆在同一個 Transaction 裡,這需考量如何界定交易區段範圍 (ScopeId):
網站系統:
網站啟動階段還未建立 Session 物件時,只能同時存在一個 Transaction,ScopeId 為 UUID。
網站建立 Session 物件後,每一個 Session 的不同 Request 只能同時存在一個 Transaction,ScopeId 由 SessionID 與本次 Request 的開始時間 HttpContext.Timestamp.Ticks 組合而成。
非網站系統:
非網站系統每一個執行緒只能同時存在一個 Transaction,ScopeId 為所在執行緒的視別子 Thread.ManagedThreadId

2. 何時建立,如何取得,以及何時關閉交易
TransactionApi 實作 IDisposable 介面,所以可以使用 using 陳述式表示如下

using(TransactionApi api = new TransactionApi()) {
	// code here ...
}

相關內容請參考 IDisposable 介面,以及 using 陳述。

TransactionApi 建立
專案程式主動於 using 陳述裡建立 TransactionApi 物件,物件產生後將存於 Dictionary<string, TransactionApi> _Transactions 集合裡。

TransactionApi 取得
透過 TransactionApi.InTransaction 判斷 _Transactions 集合裡是否存在目前 ScopeId 的 Transaction,有則取出無則建立新的 Transaction。

public static TransactionApi Scope(IntervalLogger il, string entityName)
{
	lock(_Lock) {
		TransactionApi ts;
		if(!_Transactions.TryGetValue(ScopeId, out ts)) {
			ts = new TransactionApi(il, entityName);
		}
		return ts;
	}
}

TransactionApi 關閉
由 TransactionApi.Dispose 方法關閉,並清除 _Transactions 集合裡該項物件。

public void Dispose()
{
	lock(_Lock) {
		base.DoEnd(IntervalLogger);
		if(_Transactions.ContainsKey(ScopeId)) {
			_Transactions.SafeRemove(ScopeId);
		}
		if(_SelfLogger) {
			IntervalLogger.Dispose();
			IntervalLogger = null;
		}
	}
}

<Entity 如何操作資料>
Entity 大部份是透過 Sql 與 Api 溝通,無論直接或間接與 Api 溝通,『選取』相關操作皆呼叫 Api.GetNew 方法取得新的 Api 物件,『新增』、『修改』、『刪除』則呼叫 Api.Get 方法自動取回 同一個 ScopeID 的 Api 物件,或是停用 Transaction 時的獨立 Api 物件。

鐵人賽分享列表:Kuick Application & ORM Framework
開放原始碼專案:kuick.codeplex.com
直接下載原始碼:Kuick
下載相關文件檔:C# Code Conventions and Design Guideline
相關教學影片區:Kuick on YouTube


上一篇
Kuick -- 資料庫 Transaction (一)
下一篇
Kuick -- 遮斷器 Interceptor
系列文
Kuick Application & ORM Framework34

1 則留言

我要留言

立即登入留言