請註明轉載地址: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物件,達到我們執行事務的目的。希望大家能夠從本章中學到一點東西!但是老魏卻是很認真的告訴大家,本章很重要哦!