翻译的初衷以及为什么选择《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. MongoVUE1.6.9破解启动提示System.ArgumentException: 字体“Courier New”不支持样式“Regular”

    用MongoVUE,发现报错,报错信息如下: System.ArgumentException: 字体"Courier New"不支持样式"Regular". ...

  2. web前端开发学习内容

    应该 具备的 知识技能 :懂web标准,熟练手写 xhtml css3 并符合 符合w3c标准                       代码能 兼容主流浏览器.ie6.7.8.9 ff 等.    ...

  3. pythony语法小练习

    i=[0,1] b=[2,3] print(dict([i,b]))#列表转换为字典 题目:有四个数字:1.2.3.4,能组成多少个互不相同且无重复数字的三位数?各是多少? 程序分析:可填在百位.十位 ...

  4. Git安装

    前面说了很多废话,说得再好,还不如实践一次.要想使用Git,首先得安装.这次实验主要是实践怎样安装Git. 安装与检查是否安装成功 1. 在Linux(Ubuntu)上安装 上篇的Git简介已经介绍过 ...

  5. 与Android应用程序相关的文件目录都有哪些?(转载)

    与Android应用程序相关的文件目录都有哪些? | 浏览:1312 | 更新:2014-09-28 19:43 | 标签:android 一.方法介绍:   每个Android应用程序都可以通过Co ...

  6. JS冒泡排序(div)

    更生动的排序动画. 通过改变div的高度来实现排序,通过闭包来实现for循环的睡眠时间. <!doctype html> <html lang="en"> ...

  7. 分布式大数据高并发的web开发框架

    一.引言 通常我们认为静态网页html的网站速度是最快的,但是自从有了动态网页之后,很多交互数据都从数据库查询而来,数据也是经常变化的,除了一些新闻资讯类的网站,使用html静态化来提高访问速度是不太 ...

  8. PHP通过加锁实现并发情况下抢码实现

    需求:抢码功能 要求: 1.特定时间段才开放抢码: 2.每个时间段放开的码是有限的: 3.每个码不允许重复: 实现: 1.在不考虑并发的情况下实现: function get_code($len){ ...

  9. Oracle Flashback 闪回

    Oracle 的闪回技术是一种数据恢复技术,仅能对用户逻辑错误进行恢复, 闪回针对的是提交commit的事务,没有提交的事务,使用rollback 1.闪回版本查询 Flashback Version ...

  10. LINK : fatal error LNK1123: 转换到 COFF 期间失败: 文件无效或损坏

    同时安装了VS2012和VS2010,用VS2010 时 >LINK : fatal error LNK1123: 转换到 COFF 期间失败: 文件无效或损坏 问题说明:当安装VS2012之后 ...