一、上章回顧
上篇我們簡單講述了服務層架構模式中的幾種,並且講解了服務層的作用及相關的設計規範,其實我們應該知道,在業務邏輯層中使用領域模型中使用服務層才
能發揮出最大的優勢,如果説我們在業務邏輯層還是使用非領域模型的模式話,服務層的作用僅體現在解耦作用。其實在業務邏輯層採用領域模型時,我們前面説的持
久化透明的技術,其實我們可以通過服務層來做,我們在服務層中處理領域對象信息的持久化操作。當然本篇可能不會深入討論持久化透明的具體實現,後面會單獨開
篇來講述,我們先來回顧下上篇講解的內容:
層的相關內容,請參考:系統架構師-基礎到企業應用架構-服務層,後續我們將會對一些前端的服務層還會進行擴展的講解,請大家提出報告意見和建議。
二、摘要
本篇將主要以系統中與數據庫存儲介質進行交互的數據訪問層進行詳細的介紹講解,我想這塊也是大家比較熟悉也是經常在項目中一定會用到的部分,我們知道
數據訪問層通常我們都把這塊的內容提升出來,寫成通用的類庫,在我們前面講解的分層架構的系統中,基本上可以説業務對象中的數據都要通過數據訪問層將業
務數據持久化到存儲介質中。其實目前有很多的好的ORM框架已經很好的實現了數據訪問層,而且得到了很廣泛的應用,當然我們本篇也會以這些通用的框架為例舉
例説明數據訪問層中的一些設計模式。本章將會以下列幾個典型關注點展開去講:
1、數據訪問層的職責及與其他組件的交互。
2、如何設計自己的數據訪問層。
3、實現數據訪問層必須滿足的4個基本要求,持久化CRUD、查詢服務、事務服務、實現併發等。
4、結合目前流行的幾類框架分析框架提供的數據訪問層功能的優劣。
下面我們將針對上面的幾個關注點依次展開去説,希望能通過本文的講解,讓您對數據庫訪問層有個更深刻的認識和了解。
三、本章大綱
1、上章回顧。
2、摘要。
3、本章大綱。
4、數據訪問層介紹。
5、如何設計數據訪問層。
6、實現數據訪問的四項原則。
7、本章總結。
8、系列進度。
9、下篇預告。
四、數據庫訪問層簡介
本節將會主要針對數據訪問層的功能及職責進行講解,分析之前在業務邏輯層中的四種模式與數據訪問層之間的關係。我們閒來看看數據訪問層與業務邏輯層中的
四種模式之間的關係。
我們在本節中的講解主要是以領域模型為例進行分析講解,因為只有領域模型模式,我們才能將數據訪問層抽離出來,分成單獨的層,這樣能夠做到領域對象持久
化透明。接下來我們來看看數據訪問層都需要提供什麼要的功能及數據訪問層本身的職責是什麼。
數據庫訪問層是唯一知道如何操作存儲介質的入口,可以這麼來説,基於數據訪問層之上,我們調用數據庫訪問層提供的方法,我們就能完成數據的存儲與讀取,
所以我們可以知道,數據訪問層應該是與數據庫直接是獨立的。還有就是我們的數據訪問層如何能實現不同類型的數據庫的動態的切換,而我們不需要修改任何的程序
功能等,可能我們在開發的過程中都會遇到這樣的問題。所以我們希望可以對數據訪問層完成動態的配置,通過不同的配置項完成對象數據庫訪問的切換,這裏我想大
家都是比較熟悉的,通過XML配置文件來完成數據庫的切換,前面我們説了我們的需求,是必須實現無縫的數據庫的切換,那麼我們如何實現呢,這裏我們可以通過定
義一個數據庫訪問接口,然後通過實現不同的數據庫的細節,來實現這樣的切換。目前很多流行的框架都是採用這樣的方式來實現數據庫的動態切換。當然有時候我們
的項目中肯能不讓我們使用開源的通用框架,這時候我們可能就需要自己去實現這些數據訪問層的具體細節。
當然數據訪問層都必須能夠將應用程序中的數據持久化到存儲介質中,通常我們使用的數據都是關係型的數據庫,但是我們知道我們在程序的開發中,通常採用的
模型都是對象模型,那麼如何實現對象模型與關係模型直接的互相的轉換就顯得非常的重要。當然這是數據訪問層的重要功能。通常來説,業務邏輯層及服務層不瞭解
數據庫訪問的具體細節,他們都需要通過數據訪問層來實現數據的交互。一般來説在領域模型中,數據訪問通常都是在服務層中進行調用的,而業務邏輯層並不關注數
據持久化,所以我們前面説的持久化透明的方式也是由此方式來實現。
上面我們囉嗦了一大堆,基本上説了數據訪問層的基本需求功能:
下面我們來看看數據訪問層的幾個基本職責
首先、數據訪問層應該提供基本的持久化操作CRUD的操作,我們知道數據訪問層是唯一能操作數據庫的一層,因為我們在設計時需要注意,系統的其他層不能
包含操作數據庫的相關功能
相應的持久化操作方法,當然這不是唯一方式,可行的方式有很多。我們後面會詳細討論。
其次、應該提供能夠滿足類中信息的讀取操作,一般情況下來説我們經常使用的是,根據主鍵查詢某個對象的信息,或者是查詢所有的記錄,或者是根據條件返
回滿足的集合。
領域模型中肯定會有引用對象的情況,那麼對於這樣的情況我們如何來做呢?我們一般是通過延遲加載的形式來處理這樣的要求,我們後面會依次講解。我們來看看上
面的通用形式的簡單代碼格式:
view source
?
01
public class QueryHelper
02
{
03
/// <summary>
04
/// 根據主鍵返回對象
05
/// </summary>
06
/// <typeparam name="T"></typeparam>
07
/// <param name="key"></param>
08
/// <returns></returns>
09
public T GetObjectByKey<T>(object key)
10
{
11
return default(T);
12
}
13
14
/// <summary>
15
/// 獲取指定類型對象的總記錄數
16
/// </summary>
17
/// <typeparam name="T"></typeparam>
18
/// <param name="key"></param>
19
/// <returns></returns>
20
public int GetCount<T>(object key)
21
{
22
return 0;
23
}
24
25
/// <summary>
26
/// 返回指定類型的所有對象集合
27
/// </summary>
28
/// <typeparam name="T"></typeparam>
29
/// <returns></returns>
30
public List<T> GetAll<T>()
31
{
32
return new List<T>();
33
}
34
}
再次、數據庫訪問必須提供事務的管理,可以説不提供事務操作的數據訪問層就沒有辦法使用,因為這樣的數據訪問層構建的系統是不安全的。特別是批量持久
化的過程中,事務不但能夠減少與數據庫操作的次數,而且根據事務的四個特性可以提供更好的安全性。我們來回顧下事務的四個特性:
“工作單元”來實現事務管理的,工作單元后面會講述到工作單元內提供的方法及事務性。
最後、數據訪問層必須提供處理併發的功能,我們在系統訪問的人較多的情況時肯定會出現併發的情況,數據訪問層如何處理這樣的情況就顯得極其重要了,在
一個多用户併發的系統中,通過前面提到的事務來處理,這時候可能就會出現數據庫完整性的問題,例如這樣的情況,一個用户現在在編輯自己的個人信息,例如將生
日修改為1985年3月20日,這個用户對應的ID是298,這時候他只是修改了,但是還沒有提交,此時管理員也修改了,比如説修改了ID為298的這個用户信息的地址或
者其他信息,並且提交,此時,用户將自己編輯的生日提交了,那麼數據庫中對應的ID為298的數據信息就會是最新修改的數據信息,那麼之前管理員修改的數據信息
就會發生丟失,雖然是修改的可能字段不是同一個字段,這就和我們底層實現的數據訪問層有關,當然如果説我們在數據訪問層實現了,只更新修改過的數據列的值的
話,那麼可能不會存在這樣的情況,當然這就和我們底層實現的數據訪問層的機制有關。
下面我們通過圖形的方式來説明,更容易理解:
我們來看看可能的幾種方案,可以對這樣的併發做出相應的處理?
有更好的辦法,可以告訴我,不勝感激。
當然上面的四個基本職責是我們在數據訪問層必須提供的,還應該提供緩存機制,延遲加載等等包括一些性能方面的優化的設計等,這些都在後面講解吧。
下面我們來看看數據訪問層與其他層直接的關係與交互,我們前面説過,在領域模型下,業務邏輯層中的數據的持久化都是通過服務層來完成的,下面我們來看
看各層之間的關係。我們先來看看服務層與數據訪問層之間的關係。
現業務流,然後通過調用數據訪問層將業務流中的相應數據進行持久化,通過數據訪問層來初始化領域模型。置於直接在表現層中使用數據訪問層的功能,我們通常是
不推薦這樣做的,一般我們不會這麼做的,我這裏就不詳細的闡述。
五、如何設計數據訪問層
本節將詳細的講述如何設計出自己的數據訪問層,滿足上述的幾個基本要求,那麼可以説就算完成了基本的數據訪問層的功能,其實如果我們從頭開始開發一個
這樣的數據訪問層將是非常大的工作量,目前流行的很多的ORM框架已經提供了豐富的數據訪問層的功能,能夠非常好的滿足上述的幾項職責。當然本節還是會結合代
碼來説説數據訪問層的具體實現。
我們前面講述了數據訪問的3個基本的功能需求,數據庫獨立性,可配置性及持久化對象模式(對象模型與關係模型的轉換),我們這裏先看如何實現數據庫的獨立
性,我們提出滿足數據庫的無縫遷移,通過XML配置文件,配置不同的數據庫鏈接來實現這樣的功能,那麼首先我們需要定義針對不同數據庫具體實現,才能完成後續
的操作,既然我們這裏要降低耦合,那麼根據我們前面的面向對象的設計原則與規範知道,我們推薦使用面向接口的編程的方式,來儘量的降低耦合性。我們來看看具
體的代碼。首先我們定義一個通用的數據訪問層的接口。
view source
?
01
/// <summary>
02
/// 數據訪問層統一接口
03
/// </summary>
04
public interface IDALInterface
05
{
06
//CUD 持久化操作
07
/// <summary>
08
/// 創建新的對象
09
/// </summary>
10
/// <param name="model"></param>
11
/// <returns></returns>
12
int Create(object model);
13
/// <summary>
14
/// 更新對象
15
/// </summary>
16
/// <param name="model"></param>
17
/// <returns></returns>
18
int Update(object model);
19
/// <summary>
20
/// 刪除對象
21
/// </summary>
22
/// <param name="model"></param>
23
/// <returns></returns>
24
int Delete(object model);
25
//R 查詢服務
26
/// <summary>
27
/// 查詢所有記錄
28
/// </summary>
29
/// <typeparam name="T">泛型模型</typeparam>
30
/// <returns></returns>
31
IList<T> GetAll<T>() where T : class,new();
32
/// <summary>
33
/// 查詢滿足條件的集合
34
/// </summary>
35
/// <typeparam name="T"></typeparam>
36
/// <param name="whereCondition"></param>
37
/// <returns></returns>
38
IList<T> GetListByQuery<T>(WhereCondition whereCondition) where T : class,new();
39
/// <summary>
40
/// 返回總行數
41
/// </summary>
42
/// <typeparam name="T"></typeparam>
43
/// <returns></returns>
44
int GetCount<T>();
45
/// <summary>
46
/// 返回滿足條件的總行數
47
/// </summary>
48
/// <typeparam name="T"></typeparam>
49
/// <param name="whereCondition"></param>
50
/// <returns></returns>
51
int GetCount<T>(WhereCondition whereCondition);
52
/// <summary>
53
/// 根據主鍵返回對象模型
54
/// </summary>
55
/// <typeparam name="T"></typeparam>
56
/// <param name="key"></param>
57
/// <returns></returns>
58
T GetModelByKey<T>(object key) where T : class,new();
59
//事務
60
61
/// <summary>
62
/// 是否事務執行
63
/// </summary>
64
bool IsTransaction
65
{
66
get;
67
}
68
69
/// <summary>
70
/// 開始事務
71
/// </summary>
72
void BeginTransaction();
73
/// <summary>
74
/// 提交事務
75
/// </summary>
76
void Commit();
77
/// <summary>
78
/// 回滾事務
79
/// </summary>
80
void Rollback();
81
82
}
這裏定義了基本的幾個簡單方法,當然其中並沒有包括併發的處理,後面會講到這塊的處理方案的實現, 前面介紹了幾種可行的實現方式。接口定義好了之後,
數據層的具體代碼我這裏就不一一的定義貼出來了,因為每種不同的數據庫類型就要分別實現,我們這裏講解2中不同類型的實現思路吧,
插件模式
插件模式:插件模式就是通過外部配置文件中讀取要創建的組件的類型信息,然後調用組件服務,插件模式的關鍵點就是服務不會與具體實現聯繫起來,在我們的
分層結構中的解釋就是,服務層中調用數據訪問層中的組件服務,服務層不關係具體的調用方式,服務層只關心服務。而具體的數據訪問組件是通過配置文件來動態的
創建,當然這就需要使用.NET中的反射的功能。我們來看個圖形畫的描述:
體服務組件類型,通過反射工廠來動態的創建,好了我們來看看實例代碼及配置文件。
view source
?
01
<?xml version="1.0" encoding="utf-8" ?>
02
<Connection>
03
<ConnectionItem key="connectionString"
04
value="Data Source=.\SQLEXPRESS;Initial Catalog=EasyStore;User ID=sa;Password=123456" />
05
<DALType key="DALType"
06
value="DAL.SQLServer" />
07
08
<Assembly key="Assembly "
09
value="DAL.SQLServer" />
10
</Connection>
上面的配置文件中的ConnectionItem 節點中配置了數據庫訪問的鏈接字符串,DALType 定義了數據訪問層組件的類型。我們下來看看反射工廠的示例代碼實
現。
view source
?
01
public class DALHelper
02
{
03
private static IDALInterface instance;
04
05
public static IDALInterface GetDAL()
06
{
07
string assambly = XmlHelper.getVlaue("Assembly");//這裏應該是自定義的讀取XML節點的方式
08
string type = XmlHelper.getVlaue("DALType");
09
10
Assembly asm = Assembly.Load(assambly);
11
instance = (IDALInterface)asm.CreateInstance(type);
12
13
return instance;
14
}
15
}
16
17
我們接下來看看如何使用這個數據訪問層去實現相應的持久化操作:
18
19
public class TestService
20
{
21
private IDALInterface DAL;
22
23
public TestService()
24
{
25
DAL = DALHelper.GetDAL();
26
}
27
28
public void Create(Test test)
29
{
30
//相應的判定操作
31
32
//創建對象
33
DAL.Create(test);
34
}
35
}
這樣就實現了在服務層對數據訪問層的調用操作,這裏是通過接口調用的方式來實現。我們再來看看控制反轉的實現方式吧。
控制反轉
控制反轉我們在設計規範與原則中有過講解,控制反正通過動態的將組件注入到引用該組件的對象中的形式,然後讓引用該組件的對象使用組件的服務,DI依賴注
入可以看作是控制反轉的一個應用實例,我們可以把控制反轉看作是一個原則。
下面我們來看看我們如何通過控制反正的方式來實現數據訪問層的平滑遷移。當然我們知道,肯定是通過動態注入的方式來實現,當然目前主流的也有很多的IOC動
態注入框架,下面我們將會藉助一些框架來説明如何實現這樣的功能。
本文將以Enterprise Library5.0為例進行講解動態注入的形式。我們先來看看配置文件的設置
我們來看看通過一箇中間類去實現相應的註冊代碼:
view source
?
01
/// <summary>
02
/// 用於動態完成代碼注入的公共類
03
/// </summary>
04
public class IOCContainer
05
{
06
private static IUnityContainer container;
07
private UnityConfigurationSection section;
08
public void InitIOC()
09
{
10
container = new UnityContainer();
11
section = (UnityConfigurationSection)System.Configuration.ConfigurationManager.GetSection("unity");
12
13
section.Configure(container);
14
}
15
16
public static IDALInterface GetDAL()
17
{
18
return container.Resolve<IDALInterface>();
19
}
20
}
21
22
通過上述代碼我們實現了,動態的創建數據訪問層組件實例,下面我們來看看依賴注入的方式去完成相應的持久化的功能。我們來看看服務層的代碼
23
24
/// <summary>
25
/// 測試服務層
26
/// </summary>
27
public class TestService
28
{
29
private IDALInterface DAL;
30
public TestService(IDALInterface dal)
31
{
32
DAL = dal;
33
}
34
35
public void Save(Test test)
36
{
37
DAL.Create(test);
38
}
39
}
這裏我們是採用構造函數注入的方式來實現數據訪問層的動態注入的,當然除了構造函數注入,還有其他的方式,我們這裏只是舉例説明,一般來説依賴注入有
如下的幾種形式
子,我們平時也常用這些方式。
六、實現數據訪問的四項原則
在第四節中我們講解了數據訪問層的四個原則,那麼我們在自己的數據訪問層中如何實現這幾個原則呢,我想針對第一個原則持久化的原則,我們前面只是簡單
的講解如何實現數據庫的獨立性,下面我們先來看看持久化的操作,也就是我們説的CUD的操作,並不包括具體的查詢服務,查詢服務也是我們數據訪問層必須提供四
個原則之一,我們後面都會講解,我們先來看看CUD的實現。我們在做持久化服務的時候,一般情況下,我們會定義一個統一的數據訪問層接口,然後提供持久化服
務,事務等等,通常有一些數據訪問層共性的部分,我們都通過一個抽象類來實現,抽象類將實現接口中的部分功能,然後通過定義一些抽象成員函數,讓具體的數據
訪問層去實現相應的功能。我們這裏以上節我們定義的IDALInterface為例講解基類的簡單實現。
我們將原來的接口層進行相關的優化操作將CUD操作單獨抽取出來,通過ICUDMapper接口來定義
view source
?
01
/// <summary>
02
/// 數據庫持久化訪問器
03
/// </summary>
04
public interface ICUDMapper
05
{
06
//CUD 持久化操作
07
/// <summary>
08
/// 創建新的對象
09
/// </summary>
10
/// <param name="model"></param>
11
/// <returns></returns>
12
int Create(object model);
13
/// <summary>
14
/// 更新對象
15
/// </summary>
16
/// <param name="model"></param>
17
/// <returns></returns>
18
int Update(object model);
19
/// <summary>
20
/// 刪除對象
21
/// </summary>
22
/// <param name="model"></param>
23
/// <returns></returns>
24
int Delete(object model);
25
}
26
27
然後我們來看看基類接口層的簡單實現,作為所有數據訪問層的父類
28
29
public abstract class BaseDAL : IDALInterface,IDisposable
30
{
31
protected abstract ICUDMapper GetMapper();
32
33
//CUD 持久化操作
34
/// <summary>
35
/// 創建新的對象
36
/// </summary>
37
/// <param name="model"></param>
38
/// <returns></returns>
39
public int Create(object model)
40
{
41
return GetMapper().Create(model);
42
}
43
/// <summary>
44
/// 更新對象
45
/// </summary>
46
/// <param name="model"></param>
47
/// <returns></returns>
48
public int Update(object model)
49
{
50
return GetMapper().Update(model);
51
}
52
/// <summary>
53
/// 刪除對象
54
/// </summary>
55
/// <param name="model"></param>
56
/// <returns></returns>
57
public int Delete(object model)
58
{
59
return GetMapper().Delete(model);
60
}
61
62
#region IDisposable 成員
63
64
/// <summary>
65
/// 是否數據庫訪問組件資源
66
/// </summary>
67
public void Dispose()
68
{
69
}
70
71
#endregion
72
}
當然這裏只貼出實例代碼。當然我採用這樣的方式,就是利用我們之前的一篇:Step by Step-構建自己的ORM系列-開篇 這篇中的反射的思想,大家可以看看
特性+反射的思路,我這裏的數據持久化訪問器也是類似的操作,可能底層的實現就是這樣的方式。
具體的數據持久化訪問器如何動態的生成SQL語句,緩存優化等各方面的內容,我們本篇可能不會深入的講解,我還是想將這塊放在ORM系類篇深入講解。
當然我們其實可能極端的做飯就是為每個領域模型中的對象建立一個數據持久化映射器,完成映射,我這裏則是通過創建數據庫的統一模式,在具體的映射器中,
通過反射取得數據對象的映射信息。我們來看看實現的思路吧,具體代碼我就不貼了
下面我們來看看查詢服務的實現:
我想一般的系統80%的時間數據庫執行的操作是查詢,而20%的時間在完成寫入和修改的操作,當然我這裏不是絕對的説法。我們希望有一個工具幫我們自動完
成基本的查詢服務,而不是我們手動的去書寫,因為我們發現對大部分的數據集合而言,有一些共性的操作,例如獲取某個主鍵值的對象的信息,或者是獲取數據庫表
中的總行數,或者是返回數據庫表的所有記錄,並且如何將關係數據庫中的關係模型轉換為對象模型,這都是查詢服務中應該提供的基本功能。下面我們來看看簡單實
現吧。
我想我們還是參考前面的方式,我們將IDALInterface層中的查詢服務進行抽象分離,將查詢服務單獨提出來放在接口IQuery中。代碼如下:
view source
?
01
public interface IQuery
02
{
03
/// <summary>
04
/// 查詢所有記錄
05
/// </summary>
06
/// <typeparam name="T">泛型模型</typeparam>
07
/// <returns></returns>
08
IList<T> GetAll<T>() where T : class,new();
09
/// <summary>
10
/// 查詢滿足條件的集合
11
/// </summary>
12
/// <typeparam name="T"></typeparam>
13
/// <param name="whereCondition"></param>
14
/// <returns></returns>
15
IList<T> GetListByQuery<T>(WhereCondition whereCondition) where T : class,new();
16
/// <summary>
17
/// 返回總行數
18
/// </summary>
19
/// <typeparam name="T"></typeparam>
20
/// <returns></returns>
21
int GetCount<T>();
22
/// <summary>
23
/// 返回滿足條件的總行數
24
/// </summary>
25
/// <typeparam name="T"></typeparam>
26
/// <param name="whereCondition"></param>
27
/// <returns></returns>
28
int GetCount<T>(WhereCondition whereCondition);
29
/// <summary>
30
/// 根據主鍵返回對象模型
31
/// </summary>
32
/// <typeparam name="T"></typeparam>
33
/// <param name="key"></param>
34
/// <returns></returns>
35
T GetModelByKey<T>(object key) where T : class,new();
36
}
我們來看看在基類中的實現。查詢服務的相關實現
view source
?
01
/// <summary>
02
/// 查詢服務組件
03
/// </summary>
04
/// <returns></returns>
05
protected abstract IQuery GetQuery();
06
07
#region IQuery 成員
08
09
/// <summary>
10
/// 查詢所有記錄
11
/// </summary>
12
/// <typeparam name="T">泛型模型</typeparam>
13
/// <returns></returns>
14
public IList<T> GetAll<T>() where T : class,new()
15
{
16
return GetQuery().GetAll<T>();
17
}
18
/// <summary>
19
/// 查詢滿足條件的集合
20
/// </summary>
21
/// <typeparam name="T"></typeparam>
22
/// <param name="whereCondition"></param>
23
/// <returns></returns>
24
public IList<T> GetListByQuery<T>(WhereCondition whereCondition) where T : class,new()
25
{
26
return GetQuery().GetAll<T>();
27
}
28
/// <summary>
29
/// 返回總行數
30
/// </summary>
31
/// <typeparam name="T"></typeparam>
32
/// <returns></returns>
33
public int GetCount<T>()
34
{
35
return GetQuery().GetCount<T>();
36
}
37
/// <summary>
38
/// 返回滿足條件的總行數
39
/// </summary>
40
/// <typeparam name="T"></typeparam>
41
/// <param name="whereCondition"></param>
42
/// <returns></returns>
43
public int GetCount<T>(WhereCondition whereCondition)
44
{
45
return GetQuery().GetCount<T>(whereCondition);
46
}
47
/// <summary>
48
/// 根據主鍵返回對象模型
49
/// </summary>
50
/// <typeparam name="T"></typeparam>
51
/// <param name="key"></param>
52
/// <returns></returns>
53
public T GetModelByKey<T>(object key) where T : class,new()
54
{
55
return GetQuery().GetModelByKey<T>(key);
56
}
57
58
#endregion
當然根據不同的數據庫可能定義的查詢語句的格式不同,但是返回的結果的形式卻可以定義成通用的形式。這樣我們就可以實現比較通用的查詢服務,也有很好
的通用型和擴展性。當然我們這裏還可以添加分頁的支持等,只是添加的條件有限制,實現方式還是相同。
下面我們來看看數據訪問層功能必須職責之事務性,我們都知道事務性的幾大特性,通過事務性來提供數據的安全性。我們這裏給出一種思路去實現這樣的事務
性,我們在數據訪問層中定義一組事務單元,通過一個列表維護這些事務單元,當執行提交時,我們將這個事務範圍內的所有事務單元進行提交,否則不進行真正的提
交操作。我們來看看吧,我們在之前的IDALInterface中已經定義了事務相關的幾個方法,我們這裏同樣抽出來,進行分解,抽出來一個單獨的接口ITransation,具體
代碼如下:
view source
?
01
public interface ITransaction
02
{
03
/// <summary>
04
/// 是否事務執行
05
/// </summary>
06
bool IsTransaction
07
{
08
get;
09
}
10
11
/// <summary>
12
/// 開始事務
13
/// </summary>
14
void BeginTransaction();
15
/// <summary>
16
/// 提交事務
17
/// </summary>
18
void Commit();
19
/// <summary>
20
/// 回滾事務
21
/// </summary>
22
void Rollback();
23
}
基類中的代碼如下:
view source
?
01
#region ITransaction
02
03
/// <summary>
04
/// 是否事務執行
05
/// </summary>
06
public bool IsTransaction
07
{
08
get
09
{
10
return GetTransaction().IsTransaction;
11
}
12
}
13
14
/// <summary>
15
/// 開始事務
16
/// </summary>
17
public void BeginTransaction()
18
{
19
GetTransaction().BeginTransaction();
20
}
21
/// <summary>
22
/// 提交事務
23
/// </summary>
24
public void Commit()
25
{
26
GetTransaction().Commit();
27
}
28
/// <summary>
29
/// 回滾事務
30
/// </summary>
31
public void Rollback()
32
{
33
GetTransaction().Rollback();
34
}
35
36
/// <summary>
37
/// 返回事務單元列表
38
/// </summary>
39
List<TransationUnit> list
40
{
41
get;
42
}
43
44
/// <summary>
45
/// 執行事務單元的操作,執行數據操作並提交
46
/// </summary>
47
/// <param name="unit"></param>
48
void Excute(TransationUnit unit);
49
50
#endregion
51
52
/// <summary>
53
/// 事務組件服務
54
/// </summary>
55
/// <returns></returns>
56
protected abstract ITransaction GetTransaction();
事務組件中添加了特殊的事務單元,用來存儲事務執行的操作CUD,還有就是要事務執行的數據對象,當然事務對象中的CRD操作就是使用前面講解的CRD操作
的方式,我們來看看吧,我們來看看事務單元的形式。
view source
?
01
/// <summary>
02
/// 事務單元
03
/// </summary>
04
public class TransationUnit
05
{
06
/// <summary>
07
/// CUD枚舉
08
/// </summary>
09
public enum CUDEnum
10
{
11
Create,
12
Update,
13
Delete
14
}
15
16
private CUDEnum _cudType;
17
private object _model;
18
19
public TransationUnit(object model, CUDEnum cudType)
20
{
21
_model = model;
22
_cudType = cudType;
23
}
24
}
我們在事務處理中,我們只執行事務列表中的操作,置於非事務列表中的單元我們將不做任何處理,所以我們只要是事務執行的事務單元,我們必須將指定操作類
型,當然我們還可以更靈活,我們通過在事務單元中設置屬性判定是否在事務中,如果不在事務中,我們執行數據持久化操作,如果在事務中,我們則添加到事務列表
中,因為我們在提交時總會循環執行事務列表中的事務單元。當然我這裏只是一個簡單的思路,拋磚引玉,希望大家有更好的想法,可以跟我交流,或者給我提出建
議,那樣我就感激不盡了。下面我們來看看數據訪問層中的併發的問題,我們如何去應對,有沒有什麼完美的方案呢?
前面我們舉例子説過,有3中方式,我們這裏舉例説明:
通過字典存儲值發生變化的列,完成更新變化列的操作。這樣至少可以避免2人同時更新,造成最近更新的內容覆蓋先前更新的內容,雖然可能更新的是不同的數
據列,我們這裏只提供實例代碼:
另外2種實現方式也差不多,一種是在數據庫表中添加一列版本號,然後在數據更新時必須加上之前取出來的對象的版本號,然後一併同主鍵作為條件,完成更
新,這樣至少可以保證數據的完整性。另外一直就是通過對象代理,我們將要更新的對象的字段的舊值,放在代理中,然後在生成更新語句時,我們將對象代理中的舊
值也添加到where查詢條件中,這樣我們通過限制條件的方式,來維護數據的完整性。我們來看看簡單代碼:
view source
?
01
public class EntityProxy
02
{
03
public string Name
04
{
05
get;
06
set;
07
}
08
public object Tag
09
{
10
get;
11
set;
12
}
13
public int ID
14
{
15
get;
16
set;
17
}
18
public string Unit
19
{
20
get;
21
set;
22
}
23
}
假設上面的幾個數據列是我們要更新的數據庫列,那麼我們只需要在UPdate語句中,附加這四個數據庫列字段條件就好了,這樣如果發現更新結果返回的是0,那
麼代表更新失敗。通過上面的幾種形式,我們來看,如果允許的話,我們還是推薦在數據庫表中添加一個版本號的形式來處理更新,這是最高效的形式,可以通過時間
戳的形式來生成版本號。
七、本章總結
本章主要簡單闡述了,數據訪問層的基本功能,必要的四個職責,及數據庫訪問層的簡單的設計思路與實現思路,當然我這裏沒有提供完整的實現,具體的實
現,我想我會在ORM系列中提供完整的代碼,當然目前可能必須講完架構後,回頭我會拾起來那部分進行詳細的講解,好了我想本文的內容都比較淺顯易懂,大家都能
夠迅速的掌握,如果您有更好的思路或者設計方案,那麼很希望您能提出來交流,這將是我莫大的榮幸。