NHibernate實現延遲加載的主要結構:
BuildSessionFactory的時候:
1. 根據proxyfactory.factory_class的配置創建IProxyFactoryFactory對象
2. 如果use_proxy_validator配置為true(默認為true),加載實體映射元數據之後,對需要代理的 實體使用 IProxyFactoryFactory的ProxyValidator進行驗證(例如要求實體有默認構造器、方法屬性必 須為virtual類型等)
3. 為需要代理的實體類型創建IProxyFactory,並調用IProxyFactory對象的PostInstantiate方法
對每一個需要代理的實體類型都會創建一個IProxyFactory對象並緩存,創建代理對象則通過 IProxyFactory的GetProxy方法完成
AbstractProxyFactory主要是實現PostInstantiate方法,這個方法主要是將創建代理對象所需的信息 記錄下來,例如 EntityName(字符串的實體類名)、PersistentClass(代理對象的System.Type)、 Interfaces(代理對象需要額外實現的接口,例如INHibernateProxy),其他幾個記錄的屬性則為單主鍵 和組合主鍵id的get、set方法
ProxyFactory則主要實現GetProxy方法,使用Castle或者LinFu等動態代理庫以及上述信息創建代理對 象
創建代理對象:
IProxyFactory.GetProxy方法完成
1. 創建一個LazyInitializer對象。LazyInitializer實現了各個動態代理類庫的攔截器接口
2. 通過Castle、LinFu、Spring等動態代理庫創建class proxy或者interface proxy的代理對象
創建代理對象時INHibernateProxy作為一個mixin的接口,NHibernate內部用這個接口來區分代理對象 和真實對象。對這個接口的方法調用則在攔截器中處理
延遲加載:
創建代理對象時,均使用NHibernate.ByteCode.LinFu或者NHibernate.ByteCode.Castle中的 LazyInitializer類作為攔截器,因此對代理對象的方法調用都在LazyInitializer中攔截處理
ILazyInitializer接口主要用於延遲加載的相關處理
首先,對代理對象某些方法的調用不會觸發延遲加載行為,比如讀取主鍵的值、組合類型屬性的主鍵 值、沒有override的情況下調用Object基類的一些方法以及Dispose方法等,這些邏輯在 BasicLazyInitializer的Invoke方法中處理
當調用代理對象的其他方法時,觸發延遲加載行為。加載處理在AbstractLazyInitializer的 Initialize方法中完成,加載實體所需要的信息(主要有session、id、實體的類型)在創建 LazyInitializer對象時均已經提供,加載過程仍然使用一級緩存、二級緩存、數據庫這樣一個加載順序
加載的實體對象與代理對象是兩個獨立的對象,NHibernate並沒有將加載後的實體屬性值設置到代理 對象上,估計一是考慮到有interface proxy存在的情況,另外實際加載過程是比較獨立的,他會重新創 建一個真實的實體對象,會放入一級、二級緩存中,還有一點,這種處理方式 NHibernate以及client都 可以通過代理對象來得到加載後的真實對象。真實的實體對象保存在AbstractLazyInitializer 的Target 屬性中,完成加載以後,攔截器中通過反射來調用target對象的方法
下面代碼示例怎麼由代理對象得到真實對象(非NHibernate官方公布的方法,慎用):
01 ISessionFactory sf = new Configuration().Configure().BuildSessionFactory ();
02 using (ISession session = sf.OpenSession())
03 {
04 //僅創建 代理對象,沒有實際加載
05 MyUser proxy = session.Load<MyUser> (11);
06 Console.WriteLine(proxy.GetType().FullName);
07 //因為 override了ToString方法,因此下面這個調用將觸發加載行為
08 Console.WriteLine (proxy.ToString());
09 //獲取真實對象
10 NHibernate.Proxy.INHibernateProxy nhProxy = proxy as INHibernateProxy;
11 MyUser real = nhProxy.HibernateLazyInitializer.GetImplementation() as MyUser;
12 Console.WriteLine(real.GetType().FullName);
13 Console.WriteLine (real.ToString());
14 }
15 sf.Close();
16 Console.ReadKey();
下面是運行結果: