歡迎光臨
每天分享高質量文章

如何用EFCore Lazy Loading實現Entity Split

α角 與 β角

支持 現實生活 的 計算機系統,總有著兩大偏差,第一個是 現實生活 與 計算機系統 的α角,另外一個是計算機系統的 邏輯設計 與 物理設計 的β角。舉個慄子:

  • α角:假設某個公司的商業流程,我們在做計算機自動化的時候,會發生某種程度的改變。可能是用了新計算機系統,需要調整商業流程;也可能是某些商業流程,由於種種原因,沒有被計算機系統實現支持。。。
  • β角:這個比較常見,例如某個類本身是沒有什麼ID之類的屬性,而由於我們選擇了某個資料庫產品來做持久化,而資料表的主鍵用了 某某ID 這樣的欄位,於是引致我們的 類 裡面也可能會包含了 ID 這樣的屬性;或者由於需要用 SQL Server 的 資料複製 功能,從而使到我們的類加入了各種TimeStamp欄位

Entity Split

今天我們討論的Entity Split,就是屬於上述的β角。有時候,由於某些原因(例如 縱向切割 資料表),某個 類 ,它被儲存到超過一個的資料表中。例如,我們可能有一個 Customer 類,由於它的屬性比較多,於是為了提供系統性能,我們把最常用的屬性歸納到 Customers 資料表,而把那些比較少用到的屬性歸納到 CustomerOtherInfo 資料表,等等。
在用EF Core的時候,我們會在DbContext.OnModelCreating方法裡面用modelBuilder.Entity<MyEntity>().ToTable(“Tablename“);的做法來指定 BusinessEntity 與 資料表 的映射關係,但是這個只能是Entity級別的,而沒有能去到 屬性 級別啊 。如何才能做得到指定 “某Entity的某些屬性,映射到資料表A;而某些其他屬性,映射到資料表B”,這樣的效果呢?
(本篇的程式,可以在 https://github.com/kentliu2007/EFCoreDemo/tree/master/EntitySplit 上下載,我用的是 VS2017。建議可以下載之後,對照著程式來閱讀本篇)

資料表

先來看看資料表是怎樣的:

  • Clients 表的索引

  • ClientContactInfo 表的索引
  • 外鍵FK_ClientContactInfo_Clients的設置

如何用EF5/6實現 Entity Split

首先讓我們先來看看 EF5/6 是怎麼實現的。
如果用EF5/6的話,這個很簡單。因為有設計器啊,TableMapping就可以輕鬆搞定。

  • 專案檔案:
  • EF Diagram:
  • UnitTest程式:

    看,程式完全不需要考慮資料是來源於不同的兩個資料表。簡單吧?

如何用EFCore 實現 Entity Split

用EF Core,沒有設計器,怎麼搞?其實,就算有設計器,也不能和EF5/6那樣的實現方式的。
這裡我們需要先請出 EF Core的一個重大功能 Lazy Loading。這個功能從EF Core V2 開始支持。

EF Core Lazy Loading
  • 文件:https://docs.microsoft.com/zh-cn/ef/core/querying/related-data#lazy-loading
  • 它有兩種實現方式,
    • 一種是用Microsoft.EntityFrameworkCore.Proxies包,以及呼叫UseLazyLoadingProxies來啟用這個包。並且要求類裡面的NavigationProperty需要是public且virtual
    • 另外一種使用 Microsoft.EntityFrameworkCore.Abstractions 包中定義的 ILazyLoader 服務的取用。這個需要類裡面做更多的特定代碼來支持

上述兩種做法各有利弊,所以我們接下來會針對兩個做法都分別用一次。用它們來實現 基於 EF Core的Entity Split

可行性分析

通過上面分析的EFCore裡面ToTable的做法,我們知道,實際上是真的不可避免地需要有倆 Entity ,這樣才可以設置它們分別映射到不同的資料表。然後,因為有了Lazy Loading,我們可以對 Client 這個 主類 ,添加取用 ClientContactInfo 類的相應的幾個屬性。通過玩弄getter和setter的把戲。讓EFCore的Lazy Loading在getter/setter呼叫到ClientContactInfo的屬性的時候,按需裝載,這樣又可以實現Entity Split,系統性能也得到好處。

用Microsoft.EntityFrameworkCore.Proxies來實現EFCore的Entity Split
  • 專案檔案:
  • 程式:
    • DbContext:
    • ClientContactInfo:
    • Client:
    • UnitTest:

      看上面UnitTest的程式,就看出來,我們程式呼叫Client的時候,完全不需要考慮資料是來源於不同的兩個資料表。Entity Split就這樣搞定了。
      用Microsoft.EntityFrameworkCore.Proxies的缺點是,我們需要有Client.ClientContactInfo這個NavigationProperty。而且還有另外一個可能的坑(如果你嘗試呼叫Client.ClientContactInfo.GetType()就知道了,這個我們可以以後再特別弄個隨筆來吐槽一下)。
      接下來,為了維持OOP的美式咖啡口味,讓我們換個Lazy Loading的實現方法。
用Microsoft.EntityFrameworkCore.Abstractions來實現EFCore的Entity Split
  • 專案檔案:
  • 程式:
    • DbContext (這個和上面那個例子一樣,就不騙篇幅了,大家繼續參照上面那個Lazy Loading做法的貼圖就好)
    • ClientContactInfo(這個和上面那個例子一樣,就不騙篇幅了,大家繼續參照上面那個Lazy Loading做法的貼圖就好)
    • PocoLoadingExtensions (這個是直接抄微軟文件上的,所以我也不騙篇幅,大家直接參閱上述微軟文件的內容就好。網頁上查找一下PocoLoadingExtensions這個文本就能找到了)
    • Client:

      程式裡面用了一個private field來存放 ClientContactInfo的 實體,然後用了一個private的ClientContactInfo的property(通過繼續玩弄它的getter/setter的把戲來幫忙提高程式的可維護性)
    • UnitTest(這個和上面那個例子一樣,就不騙篇幅了,大家繼續參照上面那個Lazy Loading做法的貼圖就好)
      用Microsoft.EntityFrameworkCore.Abstractions的缺點是,我們的類裡面需要加入一些額外的程式(為了支持ILazyLoader )。但是好處是,Client的public屬性裡面,再也沒有ClientContactInfo這種NavigationProperty了。就真的是毫無痕跡地實現了Entity Split。

結語

怎麼樣?EF Core真的很棒,對吧?借助Lazy Loading的功能,我們花費了一些周折,如此簡單地實現了Entity Split。
當然,我本人還是希望Entity Split這個可以built-in為EF Core的一個基本功能,而不是採取借助Lazy Loading這樣的Walk Around做法。也許接下來的第N個版本,它就會實現的。畢竟”麵包會有的,牛奶會有的,一切都會有的。” 😛
下一篇,讓我們繼續討論,如何借助Lazy Loading,在用EF Core的Inheritance功能的時候,繼續保持資料表的清潔(不需要有冗餘的欄位)。敬請期待噢。 😀

相關文章:

原文地址:https://www.cnblogs.com/fatkent/p/10365659.html

赞(0)

分享創造快樂