《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/

 

posted @ 2015-05-31 21:47  china_fucan  阅读(1384)  评论(8编辑  收藏  举报