請注明轉載地址:http://www.cnblogs.com/arhat
今天老魏那個汗啊,我的ThinkPad的電源線不通電了,擦啊。明天還得掏銀子買一個!心疼啊,原裝的啊。不過話說回來,已經用了將近10年了,已經算是可以的的了。不過就是心疼啊!明天還得給我的Thinkpad找個小三,哎!
通過前面兩章的學習,我們知道了愛NHibernate中的核心技術就是懶加載,這個懶加載技術主要作用於有關系的表中,比如多對一,一對多。通過懶加載技術,我們可以很容易的獲得關聯的數據,但是懶加載卻是有一個缺點的,我們拿前面的例子來說,如果現在我們要獲得一個班級中所有的學生信息,當學生表中的數據不是很多的情況下,那么還不怎么影響效率的,如果是1000條,但是我們只需要其中的兩條,那么NHiberante不會非常智能的加載出來,而是把1000條數據全部加載進來,很顯然這不是我們想要的結果了。
如何解決這問題呢,我們在后面的HQL中來解決這個問題,本章呢,主要和大家來討論一下對NHibernate的ISession管理。由於在NHibernate中,ISessionFactory是一個重量級對象,不能用一次創建一次,而是要在整個應用程序中只創建一次,而ISession是一個輕量級的對象,但是確是線程不安全的,所以我們今天要討論的問題就是如何來管理他們。
首先我們來先分析一下原來寫的NHibernateHelper.cs
public static class NHibernateHelper { private static ISessionFactory factory = null; static NHibernateHelper() { factory = new Configuration().Configure().BuildSessionFactory(); } public static ISession OpenSession() { return factory.OpenSession(); } } }
在這個類中,我們把ISessionFactory定義成一個私有的靜態變量,由於ISessionFactory是一個重量級對象,那么我們不能每次都要創建,這樣浪費我們系統的資源的。所以,我們把它定義為一個靜態的變量(當然,也可以使用單態來寫)。然后我們在靜態構造函數中通過Configuration讀取配置文件並創建ISessionFactory對象,那么當程序運行的時候在內存中只有一個ISessionFactory對象了。
接着,我們寫了一個靜態的方法,這個方法用來得到一個ISession對象用來和數據庫打交道。但是這里需要說明的是當每一次調用OpenSession這個方法的時候,ISessionFactory都會創建一個全新的ISession對象。當然提出這個問題並不是說這樣不好,而是這樣創建出來的ISession對象是無法控制業務邏輯中的事務的(下面將會提到)。首先我們來測試一下通過OpenSession創建出來的ISession對象是不是全新的。
我們在主程序中寫一個測試的方法:
static void Main(string[] args) { NHibernate.ISession session1 = DAL.NHibernateHelper.OpenSession(); NHibernate.ISession session2 = DAL.NHibernateHelper.OpenSession(); Console.WriteLine(session1 == session2); }
從上面打印出的結果,我們可以看出通過OpenSession來創建的ISession對象都是全新的。那么這樣的話會產生一個非常嚴重的問題,就是上面老魏說的如果我們在一次業務邏輯中同時執行了多個數據庫操作,而每一次都需要打開一個ISession,那么事務是無法控制這些操作的。尤其在Web開發中,我們很有可能在一次請求過程中,執行了多次數據庫操作,而卻只需要一個事務提交,那么OpensSession是無法達到我們的要求的。所以NHibenrate為我們提供了一個新的方法用來創建ISession,這個方法就是GetCurrentSession。通過這個方法創建的ISession的生命周期是在一個上下文中,也就是說這個方法創建的ISession和當前線程以及當前請求時綁定在一起的,只要線程和請求沒有被釋放,那么着整個執行期間都是用這一個ISession。那么很顯然,GetCurrentSession正是我們需要的。
那么如何使用這個GetCurrentSession呢?我們需要在應用程序的配置文件中進行一個配置說明,現在我們打開App.config文件,給session-factory添加一個屬性節點,內容如下:
<property name="current_session_context_class">thread_static</property>
如果我們在winform應用程序中,這屬性的取值為”thread_static”,如果在web項目中,這個取值為“web”。只有我們設置了這個節點,那么我們才能使用這個方法。我們來更改一下NHibernateHelper這個方法。在提供一個方法用來獲得GetCurrentSession得到的ISession對象。
public static ISession GetCurrentSession() { return factory.GetCurrentSession(); }
好,我們現在來測試一下這個方法,看看是否能過獲得ISession對象。在出程序中,我們更改一下Main方法。
static void Main(string[] args) { Console.WriteLine(DAL.NHibernateHelper.GetCurrentSession().IsOpen); }
運行結果如下:
哦,天啊,怎么會出現異常呢?我們會發現異常信息是”No Session bound to the current context”。沒有一個Session和當前上下文綁定。這一點是初學者經常犯的一個錯誤,我們已經配置了App.config的信息,那么怎么會獲得不到呢?大家從上面的信息可以知道,我們只是配置了”要”和當前的線程綁定,但是只是“要”還沒有真正的綁定。需要我們來手動的進行一個綁定信息。
根據分析,我們來改寫一下NHibernateHelper的方法,能夠讓我們的GetCurrentSession能夠正確運行。
private static void BindSession() { if (!CurrentSessionContext.HasBind(factory)) { CurrentSessionContext.Bind(factory.OpenSession()); } } public static ISession GetCurrentSession() { BindSession(); return factory.GetCurrentSession(); }
在NHibernateHelper中,我們添加一個方法,就是用來把ISession對象和當前上下文(thread或者是web請求)進行綁定,此時我們在調用GetCurrentSession之前,首先執行一下BindSession來綁定。
沒問題了,但是我們得到的這個ISession對象到底是不是同一個呢?我們再來測試一下,更改主程序代碼:
static void Main(string[] args) { NHibernate.ISession session1 = DAL.NHibernateHelper.GetCurrentSession(); NHibernate.ISession session2 = DAL.NHibernateHelper.GetCurrentSession(); Console.WriteLine("兩個Session是同一個嗎?"+(session1 == session2)); }
很顯然,兩個ISession對象是同一個了!這樣我們解決了上面我們提到的那個矛盾問題了。
在本章中,我們討論了一下如何去管理ISession對象以及如何獲得GetCurrentSession來獲得同一個ISession對象,達到我們執行事務的目的。希望大家能夠從本章中學到一點東西!但是老魏卻是很認真的告訴大家,本章很重要哦!