翻译的初衷以及为什么选择《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. Spring集成JUnit测试

    1.程序中有Junit环境2.导入一个jar包.spring与junit整合jar包 spring-test-3.2.0.RELEASE.jar3.测试代码 @RunWith(SpringJUnit4 ...

  2. JavaScript(三) 正则表达式 以及实现的功能

    RegExp 是正则表达式的缩写.定义RegExp正则表达式 RegExp 对象用于存储检索模式. 通过 new 关键词来定义 RegExp 对象.以下代码定义了名为 p 的 RegExp 对象,其模 ...

  3. SilverLight抛出 System.InvalidOperationException: 超出了2083 的最大URI

    在SilverLight中对于抛出 System.InvalidOperationException: 超出了 2083 的最大 URI 长度 的异常 处理 其实很简单 在 EntityFramewo ...

  4. 【转】《从入门到精通云服务器》第六讲—OpenStack基础

    前五期的<从入门到精通云服务器>受到了广泛好评,收到留言,有很多读者对云计算相关的技术非常感兴趣.应观众要求,我们这期要安利一条纯技术内容.准备好瓜子.花生,随小编一起进入OpenStac ...

  5. C语言_第五章__实践(密码转换)

    1.   要求 输入China  输出 Glmre #include <stdio.h> #include <stdlib.h> int main() { char c ; c ...

  6. travis CI

    travis可对多语言持续继承,本文以nodejs 为例. 首先添加文件.travis.yml 中language: node_jsnode_js:  - "6"  - " ...

  7. JNI和NDK编程

    Java JNI的本意是Java Native Interface(Java本地接口),它是为了方便Java调用C.C++等本地代码所封装的一层接口.通过Java JNI,用户可以调用C.C++所编写 ...

  8. IntelliJ IDEA全键盘操作

    IntelliJ IDEA 如何做到全键盘操作呢? 1.自定义快捷键实现全屏操作 你可以设置自定义快捷键进入全屏操作,并实现各个窗口之间的切换.这样,你就可以告别小窗口的时代,体验全屏显示的效果了!( ...

  9. OpenCV 3.1 Set Camera Resolution 设置相机的分辨率

    在OpenCV中,有强大的处理相机事件的api,但是貌似没有直接获取相机的最大分辨率的函数,通过属性CV_CAP_PROP_FRAME_HEIGHT和CV_CAP_PROP_FRAME_WIDTH直接 ...

  10. webform 图片验证码制作

    界面:1 <%@ Page Language="C#" AutoEventWireup="true" CodeFile="Default2.as ...