《Entity Framework 6 Recipes》中文翻譯系列 (33) ------ 第六章 繼承與建模高級應用之TPH與TPT (2)


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

6-8  嵌套的TPH建模

問題

  你想使用超過一層的TPH繼承映射為一張表建模。

解決方案

  假設你有一張員工(Employee)表,它包含各種類型的員工,比如,鍾點工,雇員。如圖6-10所示。

圖6-10 包含各種類型的員工表

 

  Employee表包含鍾點工,雇員,提成員工,這是雇員下面的一個子類型。按下面的步驟,使用派生類型HourlyEmployee,SalariedEmployee和SalariedEmployee的子類CommissionedEmployee為這張表建模。

    1、在你的項目中創建一個繼承自DbContext的上下文對象Recipe8Context;

    2、創建POCO實體類 Employee、HourlyEmployee、SalariedEmployee和CommissionedEmployee,如代碼清單6-23所示;

代碼清單6-23.POCO實體類mployee, HourlyEmployee, SalariedEmployee和CommissionedEmployee

 1  public abstract class Employee
 2     {
 3         public int EmployeeId { get; set; }
 4         public string Name { get; set; }
 5     }
 6 
 7     public class SalariedEmployee : Employee
 8     {
 9         public decimal? Salary { get; set; }
10     }
11 
12     public class CommissionedEmployee : SalariedEmployee
13     {
14         public decimal? Commission { get; set; }
15     }
16 
17     public class HourlyEmployee : Employee
18     {
19         public decimal? Rate { get; set; }
20         public decimal? Hours { get; set; }
21     }
22 }

    3、在上下文對象中添加一個類型為DbSet<Employee>的屬性;

    4、在上下文對象中重寫OnModelCreating方法,配置TPH中每個派生類的鑒別值,如代碼清單6-24所示;

代碼清單6-24. 重寫OnModelCreating方法,配置TPH中每個派生類的鑒別值

 1   protected override void OnModelCreating(DbModelBuilder modelBuilder)
 2         {
 3             base.OnModelCreating(modelBuilder);
 4 
 5             modelBuilder.Entity<Employee>()
 6                         .HasKey(e => e.EmployeeId)
 7                         .Property(e => e.EmployeeId)
 8                         .HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);
 9 
10             modelBuilder.Entity<Employee>()
11                         .Map<HourlyEmployee>(m => m.Requires("EmployeeType").HasValue("hourly"))
12                         .Map<SalariedEmployee>(m => m.Requires("EmployeeType").HasValue("salaried"))
13                         .Map<CommissionedEmployee>(m => m.Requires("EmployeeType").HasValue("commissioned"))
14                         .ToTable("Employee", "Chapter6");
15         }

原理

  TPH繼承映射是一種靈活的建模技術。繼承樹的深度和廣度可以進行合理地擴展,映射也容易實現。這種方法有效率,是因為它沒有引入額外的表,不涉及join連接。

  使用Code-First來實現TPH簡單明了,因為面向對象的繼承,層次自然。

  代碼清單6-25演示了從模型中插入和獲取。

代碼清單6-25.插入並獲取Employee的派生類型

 1  using (var context = new Recipe8Context())
 2             {
 3                 var hourly = new HourlyEmployee
 4                 {
 5                     Name = "Will Smith",
 6                     Hours = (decimal)39,
 7                     Rate = 7.75M
 8                 };
 9                 var salaried = new SalariedEmployee
10                 {
11                     Name = "JoAnn Woodland",
12                     Salary = 65400M
13                 };
14                 var commissioned = new CommissionedEmployee
15                 {
16                     Name = "Joel Clark",
17                     Salary = 32500M,
18                     Commission = 20M
19                 };
20                 context.Employees.Add(hourly);
21                 context.Employees.Add(salaried);
22                 context.Employees.Add(commissioned);
23                 context.SaveChanges();
24             }
25 
26             using (var context = new Recipe8Context())
27             {
28                 Console.WriteLine("All Employees");
29                 Console.WriteLine("=============");
30                 foreach (var emp in context.Employees)
31                 {
32                     if (emp is HourlyEmployee)
33                         Console.WriteLine("{0} Hours = {1}, Rate = {2}/hour",
34                                            emp.Name,
35                                            ((HourlyEmployee)emp).Hours.Value.ToString(),
36                                            ((HourlyEmployee)emp).Rate.Value.ToString("C"));
37                     else if (emp is CommissionedEmployee)
38                         Console.WriteLine("{0} Salary = {1}, Commission = {2}%",
39                                     emp.Name,
40                                     ((CommissionedEmployee)emp).Salary.Value.ToString("C"),
41                                     ((CommissionedEmployee)emp).Commission.ToString());
42                     else if (emp is SalariedEmployee)
43                         Console.WriteLine("{0} Salary = {1}", emp.Name,
44                                     ((SalariedEmployee)emp).Salary.Value.ToString("C"));
45                 }
46             }

代碼清單6-25的輸出如下:

All Employees
=============
Will Smith Hours = 39.00, Rate = $7.75/hour
JoAnn Woodland Salary = $65,400.00
Joel Clark Salary = $32,500.00, Commission = 20.00%

 

 

6-9  在TPT繼承映射中應用條件

問題

  你想在TPT繼承映射中應用條件。

解決方案

  假設你有兩張如圖6-11所示的表。Toy(玩具)表描述一個公司的玩具產品,大部份手工制作的玩具用於銷售,一部分捐獻給慈善機構。在制作過程中,有些玩具可能會損壞。損壞的玩具將被翻新。一個質檢員決定翻新玩具最終的質量。

圖6-11 玩具(Toy)表和翻新玩具(Refurbished)表間的一對一的關系

 

  為這家公司生成報表的應用,不需要訪問用於捐獻的玩具。按下面的步驟,使用TPT繼承映射為Toy和RefurbishedToy表建模,同時過濾掉用於捐獻的手工玩具:

    1、在你的項目中添加一個ADO.NET Entity Data Model(ADO.NET實體數據模型),並導入表Toy和ReferbishedToy;

    2、刪除實體Toy與RefurbishedToy之間的關聯;

    3、右鍵Toy實體,選擇Add(增加) ➤Inheritance(繼承)。選擇Toy作為基類,RefurbishedToy作為派生類;

    4、從實體RefurbishedToy中刪除屬性ToyId;

    5、選擇實體RefurbishedToy,並查看Mapping Details window(映射詳細信息窗口),將ToyId列映射到ToyId屬性。這個值將來至基類Toy;

    6、從Toy實體中刪除標量屬性ForDonatinOnly;

    7、選擇實體Toy,並查看Mapping Details window(映射詳細信息窗口),使用Add a Talbe or View(添加表或視圖)來映射Toy實體到實體表。添加一個條件當ForDonationOnly=0;

  最終的模型如圖6-12所示。

圖6-12 Toy實體和它的派生類型RefurbishedToy實體的概念模型

 

原理

   通過在基類中應用一個條件,我們限制RefurbishedToy實例為非捐獻玩具。這種方法在如下的情況下非常有用,用一張單獨的表來實現繼承類型映射,同時使用一個固定的條件來過濾這個繼承結構。

  代碼清單6-26 演示了從這個模型中插入和獲取數據。

代碼清單6-26. 從模型中插入和獲取數據

 1  using (var context = new Recipe9Context())
 2             {
 3                 context.Database.ExecuteSqlCommand(@"insert into chapter6.toy
 4              (Name,ForDonationOnly) values ('RagDoll',1)");
 5                 var toy = new Toy { Name = "Fuzzy Bear", Price = 9.97M };
 6                 var refurb = new RefurbishedToy
 7                 {
 8                     Name = "Derby Car",
 9                     Price = 19.99M,
10                     Quality = "Ok to sell"
11                 };
12                 context.Toys.Add(toy);
13                 context.Toys.Add(refurb);
14                 context.SaveChanges();
15             }
16 
17             using (var context = new Recipe9Context())
18             {
19                 Console.WriteLine("All Toys");
20                 Console.WriteLine("========");
21                 foreach (var toy in context.Toys)
22                 {
23                     Console.WriteLine("{0}", toy.Name);
24                 }
25                 Console.WriteLine("\nRefurbished Toys");
26                 foreach (var toy in context.Toys.OfType<RefurbishedToy>())
27                 {
28                     Console.WriteLine("{0}, Price = {1}, Quality = {2}", toy.Name,
29                                        toy.Price, ((RefurbishedToy)toy).Quality);
30                 }
31             }

代碼清單6-26的輸出如下:

All Toys
========
Fuzzy Bear
Derby Car
Refurbished Toys
Derby Car, Price = 19.99, Quality = Ok to sell

 

 

 

 

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

謝謝大家的持續關注,我的博客地址:http://www.cnblogs.com/VolcanoCloud/

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM