《Entity Framework 6 Recipes》中文翻譯系列 (9) -----第二章 實體數據建模基礎之繼承關係映射TPH

翻譯的初衷以及爲何選擇《Entity Framework 6 Recipes》來學習,請看本系列開篇html

2-10 Table per Hierarchy Inheritance 建模

問題web

  你有這樣一張數據庫表,有一類型或鑑別列。它能判斷行中的數據在你的應用中表明的是什麼。你想使用table per hierarchy(TPH)繼承映射建模。數據庫

 

解決方案框架

  讓咱們假設你有如圖2-20中的表(譯註:總感受做者使用的圖,跟實際描述對不上,好比下圖應該是實體模型圖),Employee表包含hourly employees 和salaried employees的行。列EmployeeType做爲鑑別列,鑑別這兩種員工類型的行。 當EmployeType爲1時,這一行表明一個專職員工(salaried or full-time employee),當值爲2時,這一行代碼一個鐘點工(hourly employee).ide

圖2-20 一個包含hourly employees 和salaried employees的表 Employee學習

  按下面的步驟,使用TPH基於表Employee建模:ui

    一、在你的項目中建立一個繼承自DbContext的上下文對象EF6RecipesContext;url

    二、使用代碼清單2-21建立一個抽象的POCO實體Employee;spa

      代碼清單2-21.建立一個抽象的POCO實體Employee翻譯

1     [Table("Employee", Schema = "Chapter2")]
2     public abstract class Employee {
3         [Key]
4         [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
5         public int EmployeeId { get; protected set; }
6         public string FirstName { get; set; }
7         public string LastName { get; set; }
8     }

    三、使用代碼清單2-22建立一個繼承至Emplyee的POCO實體類FullTimeEmployee.

代碼清單2-22. 建立一個POCO實體類FullTimeEmployee

1 public class FullTimeEmployee : Employee
2 {
3 public decimal? Salary { get; set; }
4 }

    四、使用代碼清單2-23建立一個繼承至Emplyee的POCO實體類HourlyEmployee.

代碼清單2-23. 建立一個POCO實體類HourlyEmployee

1  public class HourlyEmployee : Employee {
2             public decimal? Wage { get; set; }
3         }

    五、在上下文中添加一個類型爲DbSet<Employee>的屬性。

    六、在上下文中重寫方法OnModelCreating,在方法中映射你的具體的employee類型到EmployeeType鑑別列。如代碼清單2-24所示.

      代碼清單2-24. 重寫上下文中的OnModelCreating方法

        protected override void OnModelCreating(DbModelBuilder modelBuilder) {
            base.OnModelCreating(modelBuilder);
            modelBuilder.Entity<Employee>()
            .Map<FullTimeEmployee>(m => m.Requires("EmployeeType").HasValue(1))
            .Map<HourlyEmployee>(m => m.Requires("EmployeeType").HasValue(2));
        }

 

注意:非共享屬性(例如:Salary和Wage)必須爲可空類型。

 

原理

  在table per hierarchy(一般縮寫爲TPH)繼承映射中,用一張單獨的表表明整個繼承層次。不像TPT,TPH同時把基類和派生類混合進同一張表的行。它們依據鑑別列來區分。在咱們示例中,鑑別列是EmployeeType。

   在TPH中,咱們設置實體配置中的映射條件,用來指明鑑別列的值讓表映射到不一樣的派生類型。咱們讓基類爲抽象類型。經過設置其爲抽象類型,我就不用提供一個映射條件,由於一個抽象的實體是不會被建立的。咱們永遠不會有一個Employee實體的實例。咱們不用在Employee實體中實現一個EmployeeType屬性。列不會用來做映射條件,通常是映射到一個屬性。

  代碼清單2-25演示從模型插入和獲取數據。

代碼清單2-25 在咱們的TPH模型中插入和獲取數據

 1  using (var context = new EF6RecipesContext()) {
 2                 var fte = new FullTimeEmployee {
 3                     FirstName = "Jane",
 4                     LastName = "Doe",
 5                     Salary = 71500M
 6                 };
 7                 context.Employees.Add(fte);
 8                 fte = new FullTimeEmployee {
 9                     FirstName = "John",
10                     LastName = "Smith",
11                     Salary = 62500M
12                 };
13                 context.Employees.Add(fte);
14                 var hourly = new HourlyEmployee {
15                     FirstName = "Tom",
16                     LastName = "Jones",
17                     Wage = 8.75M
18                 };
19                 context.Employees.Add(hourly);
20                 context.SaveChanges();
21             }
22             using (var context = new EF6RecipesContext()) {
23                 Console.WriteLine("--- All Employees ---");
24                 foreach (var emp in context.Employees) {
25                     bool fullTime = emp is HourlyEmployee ? false : true;
26                     Console.WriteLine("{0} {1} ({2})", emp.FirstName, emp.LastName,
27                     fullTime ? "Full Time" : "Hourly");
28                 }
29                 Console.WriteLine("--- Full Time ---");
30                 foreach (var fte in context.Employees.OfType<FullTimeEmployee>()) {
31                     Console.WriteLine("{0} {1}", fte.FirstName, fte.LastName);
32                 }
33                 Console.WriteLine("--- Hourly ---");
34                 foreach (var hourly in context.Employees.OfType<HourlyEmployee>()) {
35                     Console.WriteLine("{0} {1}", hourly.FirstName, hourly.LastName);
36                 }
37             }

 

代碼清單的輸出爲:

--- All Employees ---Jane Doe (Full Time)
John Smith (Full Time)
Tom Jones (Hourly)
--- Full Time ---Jane Doe
John Smith
--- Hourly ---Tom Jones

 

  代碼清單2-15,建立、初始化、添加兩個full-time employees和一個hourly employee.在查詢中,咱們獲取全部的employees,用is操做符來判斷employee是咱們擁有員工類型中的哪種。當我打印出員工姓名時,咱們指出他的類型。

  在代碼塊中,咱們使用泛型方法OfType<T>()獲取full-time employees和hourly employees.

 

最佳實踐

  在TPH繼承映射中,何時使用抽象基類,何時在實體中建立一個映射條件,存在着爭論。使用一個具體的基類的難點在,查詢出整個繼承中的實例,很是的繁瑣。 這裏的最佳實踐是,若是你的應用中不須要基類實體的實例,那麼讓它成爲抽象類型。

  若是你的應用中須要一個基類的實例,能夠考慮引進一個新的繼承實體來覆蓋基類中的映射條件屬性。例如,在上例中咱們能夠建立一個這樣的派生類UnclassifiedEmployee。 一旦有這個派生類後,咱們就能夠放心地把基類設爲抽象類型。這就提供了一種簡單的方式來規避經過在基類中使用映射條件屬性來查詢的問題。

  在使用TPH時,有幾條準則須要記住。第一點,映射條件屬性值必須相互獨立。換句話來講,就是你不能將一行,條件映射到兩個或是更多的類型上。

  第二點,映射條件必須對錶中的每一行負責,不能存在某一行不被映射到合適的實體類型上。若是你的系統是一個遺留的數據庫,且表中的行由別的系統來建立,你沒有機會條件映射,這種狀況會至關的麻煩。 這會發生什麼情況呢?不能映射到基類或派生類的行,將不能被模型訪問到

  第三點,鑑別列不能映射到一個實體屬性上,除非它先被用做一個is not null的映射條件。這一點看上去有點過度嚴格。你可能會問,「若是不能設置鑑別值,那怎麼插入一行表明派生類的數據?」 ,答案很簡潔,你直接建立一個派生類的實例,而後和添加別的實體類型實例同樣,將其添加到上下文中,對象服務會建立一行擁有合適的鑑別值的插入語句。

  此篇到此結束,感謝你的閱讀。本系列由VolcanoCloud翻譯,轉載請註明出處:http://www.cnblogs.com/VolcanoCloud/p/4490841.html                       

 

實體框架交流QQ羣:  458326058,歡迎有興趣的朋友加入一塊兒交流

謝謝你們的持續關注,個人博客地址:http://www.cnblogs.com/VolcanoCloud/

相關文章
相關標籤/搜索