廣州的九月已經入秋,天氣已經不再象前段時間那樣酷熱。在這個季節去看看喀納斯的美麗景色是一年中最佳的選擇。如果你沒有去美麗喀納斯的時間和經費上的預算,那就請隨我進入Kanas.net之旅,雖然不如去喀納斯那麼浪漫,但也是一個退而求其次的選擇。
Kanas.net Framework是一個非常便於使用的持久層架構,如果你有非常好的物件導向的基礎,使用這個架構來實現對資料的透明訪問,應該會讓你感到滿意。如果你質疑我的這個結論,那麼你先隨我啟動這個很小的樣本,我相信你會和我有相同的結論。
第一步:下載Kanas.net Framework的最新版,並且安裝到系統中。
Kanas.net Framework是一個很小的架構,安裝到系統中的內容包括核心原始碼和全部的文檔也不會超過6M。建議在安裝的時候關閉Microsoft Visual Studio.net 2003,因為需要在這個IDE中安裝一個小的外掛程式以協助你自動地將你的ERM文檔自動轉換為實體類型的原始碼。
這裡需要做一個小的說明。你從部落格園下載的安裝包中僅包括核心原始碼及部分測試代碼、MSDN風格的協助文檔、VS.NET外掛程式三個部分。對於希望瞭解Kanas.Net架構的使用者來說已經足夠了。更完整的安裝包包括一個視覺化檢視(可以從各種資料庫模型、Visio資料庫設計模型和PowerDesigner資料庫設計模型中直接匯入到Kanas.Net的實體關聯模型中)、一組PowerPoint/Word教程和一系列擴充包,由於涉及著作權License問題目前尚不開放,僅在本公司範圍內發布。
第二步:如果你的系統中安裝有VS.Net,可以直接開啟安裝後的捷徑Kanas.Net.SDK解決方案,然後你就可以看到解決方案中所包括的五個項目:
- Kanas.Utils:公用程式項目。關於這個項目的詳情我曾經寫過一篇文章來介紹。
- Kanas.Common:基礎架構項目。這個項目與我公司內部發行的版本略有差異。後者加入了三個重要的基礎架構:基於XML的DOM架構、基於XML的配置架構和資料集的自由匯出架構。
- Kanas.Framework:持久層項目。包含所有的持久化服務。
- Kanas.Contacts:連絡人項目。這個項目選自Microsoft Office的Access樣本資料庫Contact。雖然該資料庫非常簡單,所包含的關係非常少,但作為一個入門的選題可以避免很多複雜的問題。在這個項目中可以看到所有基於Kanas.net Framework的項目如何構造一個配置環境、如何通過實體關聯模型來產生相關的業務代碼並令你在業務模組中輕鬆地解決複雜的業務問題。
- Kanas.Test:測試專案。這個項目是一個單元測試項目,如果你的系統中安裝有TestDriven.net,你甚至可以在NUnit的GUI中直接Run起來,相信你能夠看到全部的綠燈。
第三步:現在先不要急於研究前面的三個項目,將注意力放到Kanas.Contacts項目上。這是整個解決方案中最重要的項目。該項目示範了Kanas.Net的幾個非常有用的配置或擴充功能:
- 通過實體關聯模型產生實體代碼
- 通過配置來初始化業務環境
- 自訂資料庫提供器
- 自訂約束子
- 自訂約束子的資料庫實現
首先讓我們先看看ER模型。在Kanas.Contacts項目中有一個Contact.erm檔案,其產生操作是“無”,但定義了自訂工具“KanasCodeGen”。請以“HTML/XML編輯器”方式開啟這個文檔,你立即可以看到整個實體關聯模型,除了資料庫模型中的表格及欄位外,還包括一些額外的內容,如領域概念性模型。
ER模型的根項目是“ERModel”,其中規定了一些主要的屬性:
- description:模型的描述。
- namespace:產生代碼的命名空間。
- codelang:產生代碼所用的語言。CSharp就是C#,VB就是VB.Net。
- workingpath:工作路徑,即產生代碼檔案的路徑。該屬性不控制碼產生外掛程式。VS.NET外掛程式總是將代碼產生到erm文檔所在的目錄下。
- layout:產生代碼布局,可選的項是Single(產生單個代碼檔案)、Chunk(每個類型單獨產生一個代碼檔案)和Build(不產生代碼檔案,直接編譯為程式集)。該屬性不控制碼產生外掛程式。VS.Net外掛程式總是將代碼產生為單個檔案。
Contact項目不包括枚舉。所以在enums下未包含任何元素。在我的另外一個項目PMC中,由於“缺陷管理”中的選項極多,居然多達十多項。
entities節中包括所有的實體。每個實體元素以entityid屬性開始的一系列屬性,name(類型名稱)、description(類型說明)、aggregated(是否需要構建集合類型)、mapping(映射到資料庫中的表名)等。每個entity節中包含若干個property元素,每個property元素則包括name(屬性名稱)、type(屬性類型)、description(屬性描述)、mapping(映射到資料庫中的欄位名稱)等屬性。
點擊“方案總管”的工具列中的“顯示所有檔案”按鈕,可以看到contact.erm文檔下有一個contact.cs文檔。開啟這個文檔可以看到所有實體的代碼。
現在我們試試修改一下erm文檔,看看產生的源碼能否自動同步。
1.開啟Contact.cs單元,找到Contact class,可以看到一個Calls屬性,說明為“擷取對電話的彙總”:
/**//// <summary>
/// 擷取對電話的彙總
/// </summary>
[MappedCollection(typeof(CallCollection), typeof(Call), 1)]
public CallCollection Calls
{
get
{
return ((CallCollection)(this.EnsuredCell.GetCollection("Calls")));
}
}
這個屬性是一個典型的一對多的彙總關係,通過該屬性收集該連絡人的所有電話記錄。這個屬性產生的原因是在Contact.erm文檔中Call實體的Contact屬性後面的Aggregated Attribute。試著刪除這個屬性,並儲存erm文檔。再回到contact.cs頁,你會發現Contact.Calls屬性沒有了。我們再一次修改erm文檔,在Call實體的Contact屬性元素後面添加一個屬性:
Aggregated=”Calls”
並儲存erm文檔。再回到contact.cs頁,你會發現Contact.Calls屬性又回來了。
erm文檔是整個業務系統中的關鍵文檔。利用這個文檔可以實現部署上的一系列功能,例如重建資料庫。
接下來我們看看如何初始化業務環境。根據Kanas.net教程,使用Kanas.net提供的Adapters全域對象或AppContext來初始化業務環境:
- 通過註冊資料配接器或資料提供器來建立資料庫引擎;
- 通過綁定業務類型到指定的資料配接器中來獲得每個業務類型的訪問方式。
- 通過安裝業務捕獲器來幹擾資料載入及持久化。
- 通過安裝可安裝應用上下文組件來註冊綁定到應用內容相關的工作組件。
但是在Kanas.Contacts項目中沒有使用上述的方式,而是採用配置來實現。
在Kanas.Contacts項目中最主要的單元就是Services.cs。這個單元中的Services類是整個業務模組的“引導類”,所有業務環境的初始化及持久化服務都可以通過該類型來實現。該類型的構造器是私人的,所以該類必須通過靜態Factory 方法來獲得執行個體。該類型有一個靜態方法和一個靜態屬性,前者通過指定設定檔所在的路徑來初始化,後者通過靜態屬性獲得單件(Singleton)執行個體。
分析Services類的構造器,可以發現,Services通過Kanas.Utils.ConfigLoader來通過設定檔初始化業務環境。開啟設定檔Kanas.Contacts.Config,可以看出來使用者能夠自由地控制整個初始化過程。
需要說明一點:在設定檔中的adapter節,有一行配置logger的過程。平常在項目中一般可以採用類似
logger=”logfile:./logs/contacts.log”
這樣的配置來將資料庫日誌寫入指定的檔案中。但在本項目中通過自訂的日誌記錄器來實現,其目的一是可以在NUnit的GUI中立即看到資料庫訪問過程,二是可以示範如何自訂日誌記錄器或者說如何與其他日誌架構串連。
雖然Kanas.Net Framework提供了OleDbProvider供訪問Access資料庫,但是Kanas.Contacts項目卻採用了自訂的MdbProvider資料庫提供器。其目的有兩個:一是將原來OleDbProvider的構造器中的ADO.NET串連串參數改為Mdb檔案名稱參數;二是示範如何自訂基於ADO.NET的資料庫提供器。
Kanas.Contacts項目還自訂了一個約束子,這個約束子的功能是描述實體指定的日期屬性的月日與給定的日期的月日相同。這個約束子的想法是基於一個非常合理的需要:尋找生日在某個日期的所有連絡人。按照Kanas.net的預設方式,無法在資料庫層面解決這個問題,只能放棄這個約束取出全部的資料,而在執行個體化環節讀出對應屬性與給定的日期進行比較以確定是否需要載入這個執行個體。因為任何一個約束子都必須實現GetDataSelected方法。顯然這種方式會損失很大的效能,因為Access訪問引擎提供了通過Month(BirthDate)和Day(BirthDate)來擷取月日。於是,我從PropertiedConstraint派生了一個DayConstraint,並且實現了DayConstraint的CriteriaBuilder,在資料提供器的Init方法中加入這個CriteriaBuilder,最終讓這個約束子通過資料庫查詢來實現而不是通過運行時的匹配。
第四步:運行單元測試。
在Kanas.Test項目中,有一個TestBase作為所有測試類別的基類。建立這個基類的好處是共用統一的初始化過程。從這個TestBase類派生了四個測試類別:
Basic:基本測試。該測試包含了三個測試案例:
LoadingTest:載入測試。該測試包含七個測試案例:
- 基本載入
- 載入已婚的連絡人
- 按字串約束子來載入
- 按嵌套的約束子來載入
- 按反引用的日期類型約束子載入
- 懶惰載入
- 按生日載入
AggreatedTest:彙總測試。該測試包含一個測試案例:
DataTableBuildTest:資料表產生測試。
- 基本視圖測試
- 帶快速查詢的視圖測試
- 帶巢狀型別快速查詢的視圖測試
OK,Come on,Good lucky!