?????? CodeSmith是我們常用的代碼生成工具,其跟據不同的模板生成不同代碼的方式能大大加快我們的項目開發,減少重復勞動。NHibernate模板就是其常用模板之一。從這里可以下載到最新的模板文件。現在最新的版本為NHibernate-v1.2.1.2125,可以生成NHibernate1.2,2.1,3.0的代碼。我下載的稍早,是NHibernate-v1.1.7.2056,最高生成2.1的代碼,不過大同小異,就以我下載的版本來進行分析吧。
?
? ? ? 基本生成操作就不說了,官網上有講解的視頻,也有大把的網友做了專門的教程。代碼生成后,主要分為五大塊:
????? 1.Base塊,也就是基類塊,這里放有BusinessObjects塊,ManagerObjects塊,UnitTests塊的基類,還包括一個作者提供我們的一個管理NH的ISession的小模塊:NHibernateSessionManage塊。
? ? ? 2.BusinessObjects塊,也就是我們常說的實體(Entity/Model)塊。里面放置著與數據庫表一一對應的各個實體。
? ? ? 3.HbmMaps塊,NH必用,不多講。
? ? ? 4.ManagerObjects塊,也就是我們常說的BL層,里面包括了對每個實體的操作類,命名方式類似于XXXManage等等。另外還有一個小工廠,通過不同的方法實例化不同的管理類,比較簡單,不表。
? ? ? 5.UnitTests塊,測試用例塊,這不是我們今天談的重點,故不多述。
?
? ? ? 從上面可以看到,NH模板生成的代碼是典型的三層架構式代碼,采用的架構模式是活動記錄式。 更詳細的概念描述請自行Google,或者參見我另兩遍博文:業務邏輯架構模式(事務腳本,表模塊,活動記錄,領域模型),再談業務邏輯架構模式(事務腳本,表模塊,活動記錄,領域模型)。
? ? ? 由于Base塊里面的基類與下面三塊里的子類的關系較大,所以我將按照BusinessObjects塊,ManagerObjects塊,NHibernateSessionManage塊的順序來講述,在講述的過程中直接將基類一起表述。例子就參見下面的一張圖,其中Aim是實體類,AimManage是其對應的管理類。
? ? ??
? ? ? 首先是BusinessObjects塊,其實我們更常見的叫法是實體層。Aim是我們自己的實體,BusinessBase<T>是其基類,其中T這個泛型是指主鍵的類型,如果是聯合主鍵,這里則不會是基本類型,而是會單獨生成一個類,這個類的屬性與聯合主鍵的每一個子元素一一對應。BusinessBase<T>實現了IBusinessBase<T>接口,在這個接口里規定了一個很重要的屬性:Id,其類型由T來指定,這表明了所有表的主鍵的在代碼里都叫Id。當然,如果實際的表中的主鍵不叫Id也是沒有關系的,實體的配置文件hbm.xml可以解決這個問題。這個接口還有兩個方式:GetHashCode和Equals,比較簡單,不多表。
?
? ? ? 再來看ManagerObjects塊,對于這我們也有更常見的叫法:BL層或者是業務邏輯層。
?????
????? 從上向下講,最開始是個IManagerBase<T, TKey>接口,其中T是指其管理的實體類別,TKey是其管理的實體的主鍵類型。在這個接口中定義了常見的實體操作方式:增刪改和各種形勢的通用的查詢。ManagerBase<T, TKey>類則是其具體的實現。IAimManage是具體實體管理類的接口,里面定義了具體實體所具有的特定的操作方法,注意,這里繼承了IManagerBase<T, TKey>接口,這是很重要的一點,等下表述。最后是AimManage類,其繼承了ManagerBase<T, TKey>類和IAimManage接口。從前者獲取通用操作的實體,從后者獲取定義的特定操作并由自己來實現。這里就要講為什么IAimManage接口要繼承IManagerBase<T, TKey>。從編譯的角度來講,前者不繼承后者依然可以通過編譯,但從我們架構代碼的角度來講,當編碼中我們要操作AimManage類時,有兩種方式,一種是直接通過本類型操作:
一種是通過接口操作:
為了減少代碼耦合,通過接口操作是比較理想的方式。如果前者不繼承后者,當出現后者的代碼時,我們就無法通過manage來操作AimManage類從ManagerBase<T, TKey>類繼承到的通用操作了。換種說法,由于ManagerBase<T, TKey>類繼承并實現了IManagerBase<T, TKey>接口,AimManage類又繼承了ManagerBase<T, TKey>類,那么實際上AimManage類間接繼承并實現了IManagerBase<T, TKey>接口。現在AimManage類又繼承并實現了IAimManage接口,從多繼承的角度來講,各個接口只能操作其子類從本接口繼承而來的屬性與方法,如下圖:
?
如果IAimManage接口沒有繼承IManagerBase接口,那當代碼是這么寫的時候:
變量manage是無法執行A()方法的,因為A()方法從IManagerBase接口而來。
?
? ? ? 最后看看NHibernateSessionManage塊,這是作者為我們提供的一個Session管理模塊,包括兩個類與兩個接口。這個模塊通過接口的方式與其它模塊交互的少,故不多表,主要講講兩個類的使用:NHibernateSessionManager類與NHibernateSession類。NHibernateSession類是作者為我們重新封裝的Session類,他將始的ISession接口封裝了進去,目的是代替NH原始的ISession接口。里面有兩個重要的成員ISession和ITransaction。其中ISession就是NH的原始的ISession接口。由于他在這里將原始的ISession接口封裝了進去,通過其操作事務就不是很方便,于是作者就使用成員ITransaction將ISession內的ITransaction引用出來,并寫了一大堆方法來完成對事務的操作。還有一個方法:GetISession()來獲取原始的ISession。
?
????? NHibernateSessionManager類是具體的Session管理類。他通過靜態變量Instance實現了單例模式。他有一個重要的屬性Session和一個重要的方法CreateISession()。通過Session屬性來獲取本次操作的NHibernateSession類。可以看到,作者將每次操作的NHibernateSession放到了緩存中(webForm與winForm放置的地方不同),提高了性能。CreateISession()方法則是真正獲取原始ISession的方法,NHibernateSession類的GetISession()方法調用的也是他。
{
????if?(iSession?==?null)
????????iSession?=?NHibernateSessionManager.Instance.CreateISession();
????return?iSession;
}
?????? 回到ManagerBase<T, TKey>類,在其兩個構造函數中:
????:?this(NHibernateSessionManager.Instance.Session)?{?}
public?ManagerBase(INHibernateSession?session)
{
????this.session?=?session;
????this.session.IncrementRefCount();
}
?????? 默認就是調用NHibernateSessionManager類單例實例的Session屬性,或者通過自定義INHibernateSession來注入。
?
? ? ? 以上就是代碼分析的全過程,可以看出,生成的代碼精干緊湊,使用方便,是我們學習三層架構模式,活動記錄模式和生產實踐的良好示范。