[NHibernate] Session 與 Lazy Loading

Posted by Harry Chang on December 14, 2016

前言

NHibernate 最有特色的一個機制就是「Session」,一般來說我們透過NHibernate 做CRUD 都是對Session進行操作,

這邊不深入探討「Session」底層是如何運作的,有興趣可以參考這篇文章

這篇文章是要探討Session 與 LazyLoading 共同運作下所發生什麼問題。

前情提要

產品的 Service Layer 做完應做的商業邏輯後,會Call流程引擎,流程引擎也會做一些流程資料流的商業邏輯,

這邊就簡稱 Flow Layer ,畫簡單流程圖會像下面這張圖,

當然,從Controller Call Service Layer 的那個瞬間,就已經進入 NHibernate 的 Session 控管之下了。

問題探討

問題:

我們在Service Layer 透過 Session 儲存了一個實體「A」以及「B」,「A」與「B」是一對一 映對的關係,

Service Layer 的程式碼如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
    public class Service1
    {
      public void Active(){
        //實體「A」存檔
        Session.Save(new A(){
         Pk1="1234"
           //Data...
        });

        //實體「B」存檔
        Session.Save(new B(){
           //Data...
          Pk1="1234"
        });

        //Call Flow Service
        FlowService.Do("1234");     
      } 
    }

Flow Layer 的程式碼如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
    public class FlowService
    {
      public void Do(string pk){
        //透過 Session Get實體「A」
        var A=Session.Get<A>(pk);

        //透過 Lazy Loading Get 實體「B」
        var B=A.B; 

        //透過 Session Get實體「a」
        var B1=Session.Get<B>(pk);
      } 
    }

有趣的事情發生了,「B」竟然是空的!但 「B1」有值,

所以代表「B」的實體確實有被新增到 Session 裡面,但卻無法透過 實體「A」的 Lazy Loading 取得到。

那我們再做一個實驗:

Service Layer:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
    public class Service1
    {
      public void Active(){
        //new 一個 實體 A出來
        var A= new A(){
         Pk1="1234"
         //Data...
        };

        //把A的映對B做初始化
        A.B=new B(){
         Pk1="1234"
         //Data...
        };

        //實體「A」存進Session
        Session.Save(A);

        //Call Flow Service
        FlowService.Do("1234");     
      } 
    }

Flow Layer:

1
2
3
4
5
6
7
8
9
10
11
12
13
public class FlowService
    {
      public void Do(string pk){
        //透過 Session Get實體「A」
        var A=Session.Get<A>(pk);

        //透過 Lazy Loading Get 實體「B」
        var B=A.B; 

        //透過 Session Get實體「B」
        var B1=Session.Get<B>(pk);
      } 
    }

Flow Layer 的程式碼都沒有動,唯一的差別是 Service Layer 的程式碼,

我們先把「B」初始化指給「A」,再一次存進Session 底下,

但這時候「B」與「B1」都有值了,為何這時候Lazy Loading 就成功了???

問題解析

再讓我們仔細挖掘下去,透過Profiler 發現其實Lazy Loading 的機制並沒有啟動,

原來是NHibernate 在 Get 的時候,直接把我們剛剛在Service Layer 儲存的物件,再丟回來給我們,

也就是因為這樣,在「A」有指定「B」的情況下,我們可以直接透過「A」拿到「B」,而沒有指定的情況下「A」就拿不到「B」,

而這種情形下的實體,NHibernate 也不會再幫我啟動Lazy Loading 的機制了。

這樣的做法對系統效能上是良好的,因為Session 裡面本身就有我們的資料,何必再花一次連線回去要資料呢?

看起來不是很好懂,可以直接動手試試看,就可以了解其中的端倪。



icon_wechat.png