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

相关文章
相关标签/搜索