摘要:EF4程式碼優先開發——自訂資料庫模式的映射
[原文發表位置]:Entity Framework 4 「Code-First」: Custom Database Schema Mapping
[原文發表時間]:2010/7/23 10:36 PM
上周我發表了一篇關於使用Entity Framework 4進行程式碼優先開發的部落格,EF的這種程式碼優先功能提供了非常好的以程式碼為中心的開發工作流模式,它可以做到:
· 不需要打開一個設計器或定義XML映射文件就可以進行開發。
· 定義模型物件時只需寫一個簡單的類,而不需要指定基底類別。
· 使用「慣例高於配置」的方式實現資料庫持久化,不需要明確配置任何資訊。
在上周的部落格中介紹了如何使用預設的EF4慣例來實現資料庫的持久化映射。這些預設的慣例適用於任何新建的應用程式,並且不需要用戶做任何配置就可以實現類和資料庫之間的映射。
在今天的部落格中會介紹如何自訂這些持久化映射規則,以適用用戶的各種資料庫結構(Schema)。這對於涉及到現有的資料庫的場景是非常有用的(它們的結構已經定義好,理論上是不能改變的),另外,如果希望自己的模型在關聯式資料庫中以不同的方式進行保存,它也適用於這種場景。
快速回顧NerdDinner範例
在上周的部落格中介紹了如何建立一個簡單的「NerdDinner」程式,並且Demo了使用EF程式碼優先開發在處理資料上效率的提升。
下面建立了兩個模型類別用來表示應用程式裡的資料。它們是「簡單的CLR物件」(也就是「POCO」)只使用標準的.Net資料型別:
然後建立一個「NerdDinner」類來實現這些類和資料庫之間的映射。「NerdDinner」繼承自EF程式碼優先庫提供的DbContext類,它有了兩個公開的屬性:
我們使用預設的EF4程式碼優先慣例規則實現資料庫的持久化。這意味著「NerdDinners」類中的「Dinners」和「RSVPs」屬性只能和資料庫中同名的表相映射。「Dinners」和「RSVPs」模型類別中的每個屬性和資料庫表「Dinners」和「RSVPs」中的每列映射。
下圖是資料庫中「Dinners」表的定義:
下圖是資料庫中「RSVPs」表的定義:
使用EF4自訂資料庫持久化映射
EF4的程式碼優先開發可以選擇用自訂資料庫持久化映射規則,它提供了替代方案來配置類與資料庫的映射。
有多種方法可以實現。最簡單的就是覆寫DbContext基底類別的「OnModelCreating」方法:
上面的「OnModelCreating」方法會在應用程式第一次使用「NerdDinners」類時被叫用,它接受一個 「ModelBuilder」參數。這個「ModelBuilder」就是用來自訂我們模型對象的資料庫持久化規則的。下面來看一些例子學習實現方法。
應用程式運行時EF只叫用「OnModelCreating」方法一次——然後就會自動快取「ModelBuilder」的結果。這樣不會因為每次都要初始化NerdDinners類而影響建立模型的性能,用戶不需要對應用程式做任何快取優化就能得到非常好的性能。
場景一:自訂表格名稱
現在介紹使用OnModelCreating來達到自訂我們模型的資料庫持久化的一些方式。先看一種常見的情況——現在想要映射模型的類到資料庫表格中,但是這個表格名稱與我們想要映射的類別名稱是不同的。
例如:假如這個資料庫使用了以「tbl」作為表格名稱前置詞的命名規範,這樣在資料庫中表格名稱就會是「tblDinners」而不是「Dinners」:
但現在我們希望將模型類別「Dinners」 映射到表「tblDinners」中,而且不想添加任何資料持久化的屬性:
我們可以通過覆寫NerdDinners類中的「OnMedelCreating」方法來實現自訂持久化映射,並通過如下方式指定一個自訂映射規則:
上面OnMedelCreating()方法中使用的是流水線式 API設計方法—一種使用方法鏈來編寫更流暢可讀程式碼的API設計模式。這裡使用ModelBuilder物件指明映射「Dinner」類到「tblDinners」表中。
我們僅僅需要寫這些程式碼而已。現在應用程式需要查詢或保存Dinner物件時就會使用「tblDinners」而不是「Dinners」表。完全不需要更新Dinner或RSVP模型類別就可以實現這個功能——它們依舊只是POCO物件而已,沒有包含持久化邏輯。
試驗上面的更改
如果你已經從之前的部落格下載了完整的NerdDinner程式碼範例,那麼可以在裡面修改,把上述的自訂OnModelCreating()方法加進去,然後直接運行,就可以看到自訂的資料庫持久化規則的運作了。
在上篇部落格中介紹了如何使用EF程式碼優先開發實現自動建立和重構資料庫。這意味著在增加上述OnMedelCreating()程式碼之後直接運行NerdDinner程式,就會發現SQL CE資料庫裡已經有「tblDinners」表了,而不是「Dinners」表。這是因為EF已經檢測到我們模型結構的變化,然後重建資料庫以適應模型的結構。EF依照OnModelCreating () 裡自訂映射規則進行更新——所以表已經是「tblDinners」了,而不是「Dinners」。
在上篇部落格發表以後,很多人問我,有沒有方法可以使EF不要為我們自動重構資料庫。我之前可能沒有介紹清楚,對於資料庫的自動建立/重構是一個用戶必須啟用的功能(並不是一直都會發生)。用戶當然也可以明確地以自己的方式建立資料庫(通過寫程式碼,SQL部署腳本,SQL管理工具之類的),只要指出資料庫連接字串就可以了——這樣EF就不會重構或建立資料庫結構了。
我之所以在上篇部落格中介紹資料庫的自動建立功能是因為它實在是太有用了,尤其是對於新專案的早期階段。當然這也不是強制的,很多人就從來都不用它。
重要的是,我們不需要修改ASP.NET MVC程式中控制器或者介面的任何程式碼。因為「Dinner」類沒有作任何改變,所以它完全不會受到資料庫持久化規則變化的影響。
場景二:自訂列/屬性的映射
現在介紹另一個常見場景——映射一個模型類別到資料庫中,並且資料庫表和列名與被映射的類和屬性名不同。
舉例來說,假設資料庫的「tblDinner」表中的所有列都以「col」做前置詞—而且這個表格名稱也和Dinner類不同:
仍需要影射「Dinners」模型類別到「tblDinners」表中—並且不想添加任何資料庫持久化屬性:
可以通過修改「OnModelCreating」方法實現這種自訂持久化,只需增加一點點映射規則:
上述程式碼中使用了與場景一相同的.MapSingleType()和.Table()方法流水線式的叫用。不同之處在於在MapSingleType()方法中還額外指定了列映射規則。這是通過傳遞一個匿名物件實現的,它可以把資料庫中表的列名與Dinner類中的屬性關聯起來。
Lambda運算式中指定的dinner參數是強型別的——這意味使用VS程式碼編輯器時,在智慧提示和編譯過程都會檢查「dinner」的屬性。VS還提供了重構支持——也就是說可以隨時修改Dinner類的屬性。你可以使用VS的重構功能自動更新映射規則,點點右鍵選單就可以了,不需要手動敲程式碼。
場景三:跨型別表格映射
資料庫中關係表的結構通常與物件導向模型類別的設計不同。在資料庫中希望通過一個很大的表來實現,但從物件導向的觀點來看,有時使用多個類相互關聯才更合理——對於一個實體,你可能希望將資料表分拆到多個對象上。
舉例來講,讓我們不使用單個「colAddr」列保存位址,而是在 「tblDinners」裡使用多個列來保存餐會的位址:
與其在「Dinner」模型類別中使用4個不同的屬性表示資料庫中這些列,我們可以直接把它們打包到一個「Address」類中,然後作為一個屬性放在「Dinner」類裡,如下所示:
注意,上述只是簡單的在「Address」類中定義了4個公開的屬性,然後「Dinner」類通過「Address」屬性來引用它。我們的模型類別仍然是簡單的CLR對象,不包含任何持久化邏輯。
可以通過修改「OnModelCreating」方法實現將這種層次性的類結構映射到資料庫中單個表的規則:
注意到這裡使用的映射方法與之前範例相同——映射表的列名到模型類別中的強型別屬性中,只是把它擴展到支持更複雜的子屬性而已。唯一不同的是叫用了modelBuilder.ComplexType<Address>()方法把Address類轉換為型別,這樣就可
以應用在映射運算式中了。
這就是實現跨型別表映射需要寫的所有程式碼。
下載添加了自訂持久化規則的NerdDinner範例
你可以在這裡下載更新版本的NerdDinner範例程式碼。你需要VS 2010(或者免費的Visual Web Developer 2010 Express)才能打開它。
你必須下載並安裝SQL CE 4才能運行上面的範例,從這裡下載EF 程式碼優先類別庫。安裝這兩個程式都不會對你的機器造成任何影響。
總結
CTP 4版本的EF 程式碼優先的實現體現了一個極佳的程式碼之上的資料處理方式。它很好很強大。希望你可以通過這兩篇文章窺其一斑。
請從這裡下載EF 程式碼優先的CTP 4版本。另外請參閱下面ADO.NET團隊的部落格學習關於EF 程式碼優先類別庫的知識:
· 資料註釋(DataAnnotation)和程式碼優先類別庫
希望這能對您有所幫助。
標籤:ASP.NET, .NET, LINQ, Community News
附:[除了寫部落格以外,我現在也使用推特(Twitter)來及時更新狀態和分享連結,您可以到這個位址「推」我一下:twitter.com/scottgu]