翻译的初衷以及为什么选择《Entity Framework 6 Recipes》来学习,请看本系列开篇

2-10 Table per Hierarchy Inheritance 建模

问题

  你有这样一张数据库表,有一类型或鉴别列。它能判断行中的数据在你的应用中代表的是什么。你想使用table per hierarchy(TPH)继承映射建模。

解决方案

  让我们假设你有如图2-20中的表(译注:总感觉作者使用的图,跟实际描述对不上,比如下图应该是实体模型图),Employee表包含hourly employees 和salaried employees的行。列EmployeeType作为鉴别列,鉴别这两种员工类型的行。 当EmployeType为1时,这一行代表一个专职员工(salaried or full-time employee),当值为2时,这一行代码一个钟点工(hourly employee).

图2-20 一个包含hourly employees 和salaried employees的表 Employee

  按下面的步骤,使用TPH基于表Employee建模:

    1、在你的项目中创建一个继承自DbContext的上下文对象EF6RecipesContext;

    2、使用代码清单2-21创建一个抽象的POCO实体Employee;

      代码清单2-21.创建一个抽象的POCO实体Employee

     [Table("Employee", Schema = "Chapter2")]
public abstract class Employee {
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int EmployeeId { get; protected set; }
public string FirstName { get; set; }
public string LastName { get; set; }
}

    3、使用代码清单2-22创建一个继承至Emplyee的POCO实体类FullTimeEmployee.

代码清单2-22. 创建一个POCO实体类FullTimeEmployee

 public class FullTimeEmployee : Employee
{
public decimal? Salary { get; set; }
}

    4、使用代码清单2-23创建一个继承至Emplyee的POCO实体类HourlyEmployee.

代码清单2-23. 创建一个POCO实体类HourlyEmployee

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

    5、在上下文中添加一个类型为DbSet<Employee>的属性。

    6、在上下文中重写方法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())
.Map<HourlyEmployee>(m => m.Requires("EmployeeType").HasValue());
}

注意:非共享属性(例如:Salary和Wage)必须为可空类型。

原理

  在table per hierarchy(通常缩写为TPH)继承映射中,用一张单独的表代表整个继承层次。不像TPT,TPH同时把基类和派生类混合进同一张表的行。它们依据鉴别列来区分。在我们示例中,鉴别列是EmployeeType。

  在TPH中,我们设置实体配置中的映射条件,用来指明鉴别列的值让表映射到不同的派生类型。我们让基类为抽象类型。通过设置其为抽象类型,我就不用提供一个映射条件,因为一个抽象的实体是不会被创建的。我们永远不会有一个Employee实体的实例。我们不用在Employee实体中实现一个EmployeeType属性。列不会用来作映射条件,一般是映射到一个属性。

  代码清单2-25演示从模型插入和获取数据。

代码清单2-25 在我们的TPH模型中插入和获取数据

  using (var context = new EF6RecipesContext()) {
var fte = new FullTimeEmployee {
FirstName = "Jane",
LastName = "Doe",
Salary = 71500M
};
context.Employees.Add(fte);
fte = new FullTimeEmployee {
FirstName = "John",
LastName = "Smith",
Salary = 62500M
};
context.Employees.Add(fte);
var hourly = new HourlyEmployee {
FirstName = "Tom",
LastName = "Jones",
Wage = 8.75M
};
context.Employees.Add(hourly);
context.SaveChanges();
}
using (var context = new EF6RecipesContext()) {
Console.WriteLine("--- All Employees ---");
foreach (var emp in context.Employees) {
bool fullTime = emp is HourlyEmployee ? false : true;
Console.WriteLine("{0} {1} ({2})", emp.FirstName, emp.LastName,
fullTime ? "Full Time" : "Hourly");
}
Console.WriteLine("--- Full Time ---");
foreach (var fte in context.Employees.OfType<FullTimeEmployee>()) {
Console.WriteLine("{0} {1}", fte.FirstName, fte.LastName);
}
Console.WriteLine("--- Hourly ---");
foreach (var hourly in context.Employees.OfType<HourlyEmployee>()) {
Console.WriteLine("{0} {1}", hourly.FirstName, hourly.LastName);
}
}

代码清单的输出为:

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

《Entity Framework 6 Recipes》中文翻译系列 (9) -----第二章 实体数据建模基础之继承关系映射TPH的更多相关文章

  1. 《Entity Framework 6 Recipes》中文翻译系列 (8) -----第二章 实体数据建模基础之继承关系映射TPT

    翻译的初衷以及为什么选择<Entity Framework 6 Recipes>来学习,请看本系列开篇 2-8 Table per Type Inheritance 建模 问题 你有这样一 ...

  2. 《Entity Framework 6 Recipes》翻译系列 (3) -----第二章 实体数据建模基础之创建一个简单的模型

    第二章 实体数据建模基础 很有可能,你才开始探索实体框架,你可能会问“我们怎么开始?”,如果你真是这样的话,那么本章就是一个很好的开始.如果不是,你已经建模,并在实体分裂和继承方面感觉良好,那么你可以 ...

  3. 《Entity Framework 6 Recipes》翻译系列 (4) -----第二章 实体数据建模基础之从已存在的数据库创建模型

    不知道对EF感兴趣的并不多,还是我翻译有问题(如果是,恳请你指正),通过前几篇的反馈,阅读这个系列的人不多.不要这事到最后成了吃不讨好的事就麻烦了,废话就到这里,直奔主题. 2-2 从已存在的数据库创 ...

  4. 《Entity Framework 6 Recipes》翻译系列 (5) -----第二章 实体数据建模基础之有载荷和无载荷的多对多关系建模

    2-3 无载荷(with NO Payload)的多对多关系建模 问题 在数据库中,存在通过一张链接表来关联两张表的情况.链接表仅包含连接两张表形成多对多关系的外键,你需要把这两张多对多关系的表导入到 ...

  5. 《Entity Framework 6 Recipes》中文翻译系列 (10) -----第二章 实体数据建模基础之两实体间Is-a和Has-a关系建模、嵌入值映射

    翻译的初衷以及为什么选择<Entity Framework 6 Recipes>来学习,请看本系列开篇 2-11 两实体间Is-a和Has-a关系建模 问题 你有两张有Is-a和Has-a ...

  6. 《Entity Framework 6 Recipes》中文翻译系列 (7) -----第二章 实体数据建模基础之拆分实体到多表以及拆分表到多实体

    2-6 拆分实体到多表 问题 你有两张或是更多的表,他们共享一样的主键,你想将他们映射到一个单独的实体. 解决方案 让我们用图2-15所示的两张表来演示这种情况. 图 2-15,两张表,Prodeuc ...

  7. 《Entity Framework 6 Recipes》中文翻译系列 (6) -----第二章 实体数据建模基础之使用Code First建模自引用关系

    2-5 使用Code First建模自引用关系 问题 你的数据库中一张自引用的表,你想使用Code First 将其建模成一个包含自关联的实体. 解决方案 我们假设你有如图2-14所示的数据库关系图的 ...

  8. 《Entity Framework 6 Recipes》翻译系列 (1) -----第一章 开始使用实体框架之历史和框架简述

    微软的Entity Framework 受到越来越多人的关注和使用,Entity Framework7.0版本也即将发行.虽然已经开源,可遗憾的是,国内没有关于它的书籍,更不用说好书了,可能是因为EF ...

  9. 《Entity Framework 6 Recipes》翻译系列(2) -----第一章 开始使用实体框架之使用介绍

    Visual Studio 我们在Windows平台上开发应用程序使用的工具主要是Visual Studio.这个集成开发环境已经演化了很多年,从一个简单的C++编辑器和编译器到一个高度集成.支持软件 ...

随机推荐

  1. GIT版本管理工具

    原文:http://blog.csdn.net/ithomer/article/details/7527877 Git 是一个分布式版本控制工具,它的作者 Linus Torvalds 是这样给我们介 ...

  2. Unity 依赖注入之二

    1. 构造子注入 1.1 构造子注入初级代码 container.RegisterType<IMyWork, MyWork>(new InjectionConstructor(new Bo ...

  3. .net 开发---windows服务

    因为想把quartz.net自动run的程式挂到windows服务中去,遇到问题记录 1.创建windows服务后,利用C:\Windows\Microsoft.NET\Framework\v4.0. ...

  4. Nodemanager Out of heap memory[fix bug全过程]

    问题: 自己写了一个yarn上的application,发现nodemanager过段时间,会out of memory退出,把nodemanager的heap memory从1G增大到2G也是无法避 ...

  5. 缺少.lib文件导致的Link2019 解决方案汇总

    环境Vs2015,  Win10 添加lib的方法在末尾 下面的错误都是我在写Direct3D程序中遇到的, 记下来方便查找 4.ws2_32.lib 3.   version.lib _GetFil ...

  6. 关于ADO.NET连接ORACLE,使用ODAC连接中的一些问题

    ADO.NET连接ORACLE时,用到ODAC组件时,有几点注意的. 1.安装的具体方法见:http://jingyan.baidu.com/article/e4511cf336ce872b845ea ...

  7. 使用VisualVM检测

    下载 https://visualvm.github.io/ 检测远程服务器 转自:http://blog.csdn.net/yangkangtq/article/details/52277794 授 ...

  8. Linux基础命令-有关于目录的命令

    1. 查看帮助: [root@oracle ~]# man cd //查看 cd 指令的帮助文档 2. 显示当前工作目录: [root@oracle ~]# pwd/root 3. 列出当前目录下的内 ...

  9. 说说js作用域

    开始就来说说作用域这个蛋疼的东西.里面可能会出现各种的问题 .先给一个简单的例子大家猜猜结果是什么 var   a="b"; function text(){ alert(a);v ...

  10. 软件工程:vs单元测试

    vs单元测试?VS?没装呢... 那么赶紧装个吧,于是跑到这去了: http://www.msdn.hk 我下个免费社区版. 安装过程没有什么需要说明的,傻瓜式安装会吗?当然中间会耗很长时间. 由于以 ...