《Entity Framework 6 Recipes》中文翻译系列 (9) -----第二章 实体数据建模基础之继承关系映射TPH
翻译的初衷以及为什么选择《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的更多相关文章
- 《Entity Framework 6 Recipes》中文翻译系列 (8) -----第二章 实体数据建模基础之继承关系映射TPT
翻译的初衷以及为什么选择<Entity Framework 6 Recipes>来学习,请看本系列开篇 2-8 Table per Type Inheritance 建模 问题 你有这样一 ...
- 《Entity Framework 6 Recipes》翻译系列 (3) -----第二章 实体数据建模基础之创建一个简单的模型
第二章 实体数据建模基础 很有可能,你才开始探索实体框架,你可能会问“我们怎么开始?”,如果你真是这样的话,那么本章就是一个很好的开始.如果不是,你已经建模,并在实体分裂和继承方面感觉良好,那么你可以 ...
- 《Entity Framework 6 Recipes》翻译系列 (4) -----第二章 实体数据建模基础之从已存在的数据库创建模型
不知道对EF感兴趣的并不多,还是我翻译有问题(如果是,恳请你指正),通过前几篇的反馈,阅读这个系列的人不多.不要这事到最后成了吃不讨好的事就麻烦了,废话就到这里,直奔主题. 2-2 从已存在的数据库创 ...
- 《Entity Framework 6 Recipes》翻译系列 (5) -----第二章 实体数据建模基础之有载荷和无载荷的多对多关系建模
2-3 无载荷(with NO Payload)的多对多关系建模 问题 在数据库中,存在通过一张链接表来关联两张表的情况.链接表仅包含连接两张表形成多对多关系的外键,你需要把这两张多对多关系的表导入到 ...
- 《Entity Framework 6 Recipes》中文翻译系列 (10) -----第二章 实体数据建模基础之两实体间Is-a和Has-a关系建模、嵌入值映射
翻译的初衷以及为什么选择<Entity Framework 6 Recipes>来学习,请看本系列开篇 2-11 两实体间Is-a和Has-a关系建模 问题 你有两张有Is-a和Has-a ...
- 《Entity Framework 6 Recipes》中文翻译系列 (7) -----第二章 实体数据建模基础之拆分实体到多表以及拆分表到多实体
2-6 拆分实体到多表 问题 你有两张或是更多的表,他们共享一样的主键,你想将他们映射到一个单独的实体. 解决方案 让我们用图2-15所示的两张表来演示这种情况. 图 2-15,两张表,Prodeuc ...
- 《Entity Framework 6 Recipes》中文翻译系列 (6) -----第二章 实体数据建模基础之使用Code First建模自引用关系
2-5 使用Code First建模自引用关系 问题 你的数据库中一张自引用的表,你想使用Code First 将其建模成一个包含自关联的实体. 解决方案 我们假设你有如图2-14所示的数据库关系图的 ...
- 《Entity Framework 6 Recipes》翻译系列 (1) -----第一章 开始使用实体框架之历史和框架简述
微软的Entity Framework 受到越来越多人的关注和使用,Entity Framework7.0版本也即将发行.虽然已经开源,可遗憾的是,国内没有关于它的书籍,更不用说好书了,可能是因为EF ...
- 《Entity Framework 6 Recipes》翻译系列(2) -----第一章 开始使用实体框架之使用介绍
Visual Studio 我们在Windows平台上开发应用程序使用的工具主要是Visual Studio.这个集成开发环境已经演化了很多年,从一个简单的C++编辑器和编译器到一个高度集成.支持软件 ...
随机推荐
- 在 case 语句中使用字符串-转
http://www.cnblogs.com/del/archive/2008/07/08/1237856.html 非常遗憾 Delphi 的 case 语句不支持字符串, 但我觉得这也可能是基于效 ...
- iOS 真机测试时报错:Provisioning profile "iOS Team Provisioning Profile: XXX” doesn't include the currently selected device “XXX”.
这几天因工作需要,去给客户演示iOS项目打包的过程.之前演示都是顺利的,但后来客户自己操作时打电话说遇到了问题,出现报错. 就过去看了一下,发现一个很陌生的错误提示: The operation co ...
- dedecms 文章页图片改为绝对路径
这几天在网站改版,想把网站做大,想做频道页二级域名,于是在做网站的过程中发现一个问题,dedecms开设二级域名后,在二级域名的文章页无法显示图片,查看源代码后发现问题,由于dedecms文章页中的图 ...
- DXUT源码阅读笔记
14.GetCapture() 函数功能:该函数取得捕获了鼠标的窗口(如果存在)的句柄.在同一时刻,只有一个窗口能捕获鼠标:此时,该窗口接收鼠标的输入,无论光标是否在其范围内.函数原型:HWND Ge ...
- 工厂模式模拟Spring的bean加载过程
一.前言 在日常的开发过程,经常使用或碰到的设计模式有代理.工厂.单例.反射模式等等.下面就对工厂模式模拟spring的bean加载过程进行解析,如果对工厂模式不熟悉的,具体可以先去学习一下工厂 ...
- 基于lcov实现的增量代码UT覆盖率检查
背景介绍 配合CppUTest单元测试框架,lcov提供了一套比较完整的工程工具来对UT覆盖率进行度量.但对有些团队来说,历史负担太重,大量的遗留代码没有相应的UT.在这种情况下,对新增代码进行覆盖率 ...
- Liquid Exception: Included file '_includes/customizer-variables.html' not found in assets/bootstrap/docs/customize.html 解决方案
执行下面这句话即可 rm -rf source/assets/bootstrap/docs/
- HDU 1907 Nim博弈变形
1.HDU 1907 2.题意:n堆糖,两人轮流,每次从任意一堆中至少取一个,最后取光者输. 3.总结:有点变形的Nim,还是不太明白,盗用一下学长的分析吧 传送门 分析:经典的Nim博弈的一点变形. ...
- jmobile学习之路 ---- 视口
当我们的浏览器在窗口最大化的时候,此时屏幕的宽度,就是我们桌面的分辨率.这个规则仅仅适用于PC! 我们试图在iPhone中输出屏幕宽度,你会发现屏幕宽度是980!居然和PC屏幕差不多大! 苹果主导的这 ...
- winfrom自定义滚动条
panel或图片什么的跟着鼠标走,这里panel自己可以加背景图或直接搞个图就行了.为了演示清楚,有个滚动条控件做对比,与自定义的同步. using System; using System.Coll ...