《Entity Framework 6 Recipes》中文翻译系列 (8) -----第二章 实体数据建模基础之继承关系映射TPT
翻译的初衷以及为什么选择《Entity Framework 6 Recipes》来学习,请看本系列开篇
2-8 Table per Type Inheritance 建模
问题
你有这样一张数据库表,它包含一些额外的信息,这些信息来到一张公共的表。你想使用Table per Type Inheritance(TPT)继承映射建模。
解决方案
假设你有两张表与一张公共的表密切相关,如图2-17所示,Businiss表与eCommerce表、Retail表有1:0...1关系。最关键的是,eCommerce表和Retail表中有关于Business表中代表业务的额外的信息。(BusinessID).

图2-17 密切相关的表
表Retail和eCommerce与表Business密切相关,它们包含一些与业务密切相关的属性。 按下面的步骤,使用TPT为这个继承(Retail和eCommerce实体继承自Business实体)建模。
1、在你的项目中创建一个继承自DbContext的上下文对象EF6RecipesContext;
2、使用代码清单2-16创建一个POCO实体Business;
代码清单2-16 创建一个POCO实体Business
[Table("Business", Schema = "Chapter2")]
public class Business {
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int BusinessId { get; protected set; }
public string Name { get; set; }
public string LicenseNumber { get; set; }
}
3、使用代码清单2-17 创建一个POCO实体eCommerce,它继承至Business类
代码清单2-17 创建一个POCO实体eCommerce
[Table("eCommerce", Schema = "Chapter2")]
public class eCommerce : Business {
public string URL { get; set; }
}
4、使用代码清单2-18 创建一个POCO实体Retail,它继承至Business类
代码清单2-18 创建一个POCO实体Retail
[Table("Retail", Schema = "Chapter2")]
public class Retail : Business {
public string Address { get; set; }
public string City { get; set; }
public string State { get; set; }
public string ZIPCode { get; set; }
}
5.在上下文对象EF6RecipesContext中添加DbSet<Businss>属性; (译注:这里如果不加构造函数,数据将会保存到默认的数据库实例SQLExpress中,导致不少读者认为数据没有保存到数据库表中,构造函数如下代码清单2-18-1所示)
public Recipe8Context()
: base("name=EF6CodeFirstRecipesContext")
{ }
原理
表Retail和eCommerce位于,与表Business关系1:0...1的 0...1这一边。这意味着,一个business可以没有额外信息,也可以有额外的Retail和eCommerce信息。在面向对象的编程中,我们有一个基类Business,两个派生类,Retail和eCommerce.
因为1:0...1的关系,在表Retail和eCommerce中,不可能有在表Business中没有与之相应的行的数据(行)。在面向对象中,派生类继承基类的属性,这是继承的核心。在table per type(常称为TPT)映射中,每个派生类都被表示成一个单独的表。
代码清单2-19 演示从模型中插入和获取
代码清单2-19 在TPT模型中插入和获取实体
using (var context = new EF6RecipesContext()) {
var business = new Business {
Name = "Corner Dry Cleaning",
LicenseNumber = "100x1"
};
context.Businesses.Add(business);
var retail = new Retail {
Name = "Shop and Save",
LicenseNumber = "200C",
Address = "101 Main",
City = "Anytown",
State = "TX",
ZIPCode = ""
};
context.Businesses.Add(retail);
var web = new eCommerce {
Name = "BuyNow.com",
LicenseNumber = "300AB",
URL = "www.buynow.com"
};
context.Businesses.Add(web);
context.SaveChanges();
}
using (var context = new EF6RecipesContext()) {
Console.WriteLine("\n--- All Businesses ---");
foreach (var b in context.Businesses) {
Console.WriteLine("{0} (#{1})", b.Name, b.LicenseNumber);
}
Console.WriteLine("\n--- Retail Businesses ---");
foreach (var r in context.Businesses.OfType<Retail>()) {
Console.WriteLine("{0} (#{1})", r.Name, r.LicenseNumber);
Console.WriteLine("{0}", r.Address);
Console.WriteLine("{0}, {1} {2}", r.City, r.State, r.ZIPCode);
}
Console.WriteLine("\n--- eCommerce Businesses ---");
foreach (var e in context.Businesses.OfType<eCommerce>()) {
Console.WriteLine("{0} (#{1})", e.Name, e.LicenseNumber);
Console.WriteLine("Online address is: {0}", e.URL);
}
}
代码清单2-19 创建并初始化Business实体实例和两个派生类实例。使用上下文中Business实体集中的Add()方法将他们添加到上下文中。
在查询中,我们迭代上下文中的Businesses实体集访问所有的businesses。对于派生类型,我们使用泛型方法OfType<T>()并通过指定具体的类型参数在实体集中过滤。
代码清单2-19的输出如下:
--- All Businesses ---Corner Dry Cleaning (#100X1)
Shop and Save (#200C)
BuyNow.com(#300AB)
--- Retail Businesses ---Shop and Save (#200C)
101 Main
Anytown, TX 76106
---- eCommerce Businesses ---BuyNow.com(#300AB)
Online address is: www.buynow.com
TPT是实体框架支持三个继承映射中的一个,另外两个分别是Table per Hierarchy(TPH,将在本章后面部分讨论),和Table per Concrete Type(TPC,见第15章)。
TPT继承映射提供了数据库方面的灵活性,作为开发人员,我们可以很容易在模型中为新加的表添加派生类型。但是,每个派生类型都会涉及一个额外的join连接,这会降低系统的性能。 在真实的应用中,我们已经看到当派生类型很多时,使用TPT继承映射所带来的性能问题。
Table per hierarchy(TPH)继承映射,将在2-10小节讲述。它将整个继承类型存储在一张单独的表中,他解决了TPT中的join连接问题,并带来了好的性能。但牺牲了数据库的灵活性。
Table per concrete(TPC)继承映射,它被实体框架于运行时所支持,但不被设计器支持。关于TPC的重要应用,请见第15章。
2-9 使用条件过滤对象集
问题
你想在实体类型上创建一个固定的条件来映射表中行的一个子集。
解决方案
假设你有一张包含账户信息的数据库表,如图2-18所示,该表有一个可为空的列DeletedOn,它保存着账户被删除的日期和时间。如果账户是激活的,列DeletedOn的值为null,你希望实体类型只代表激活的账户,也就是说,是DeletedOn没有值的账户。

图2-18 有DeletedOn列表Account表
按下面的步骤,为表Account建模,模型中实体类型只代表激活状态的账户
1、右键你的项目,选择Add(增加) ➤New Item(新建项),然后选择Visual C#条目下的Data模板下的ADO.NET Entity Data Model(ADO.NET实体数据模型)。
2、选择Generate from database 从一个已存在的数据库创建模型,点击Next(下一步)。
3、可以选择一个已存在的数据库连接,也可以选择新建一个数据库连接。
4、在选择数据库窗口,选择表Account。后勾选上确定所生成对象名称的单复数形式、在模型中包含外键列复选框。点击Finish(完成)
5、单击Account实体并查看映射详细信息窗口,如果映射详细信息窗口未显示。选择工具菜单View(视图) ➤Other Windows(其它窗口) ➤Entity Data Model Mapping Details(实体数据模型映射详细信息)。在映射详细信息窗口中单击 Add a Condition(增加一个条件),然后选择列DeletedOn, 在Operator(操作符)列中,选择Is,在Value/Property(值或属性)列中选择Null. 这样就创建了一个映射条件(如图2-18)
6、右键DeleltedOn属性并选择Delete,因为我们使用列DeletedOn作为条件映射,我们不把它映射到实体中的属性。 在我们模型中,它的值总是为Null。
原理
在示例中,我们在实体Account中应用Is Null条件来过滤行中DeletedOn列有值的行。代码清单2-20演示了从Account表插入和获取数据。
代理清单2-20 从Account插入和获取数据
using (var context = new EF6RecipesContext()) {
context.Database.ExecuteSqlCommand(@"insert into chapter2.account
(DeletedOn,AccountHolderId) values ('2/10/2009',1728)");
var account = new Account { AccountHolderId = };
context.Accounts.Add(account);
account = new Account { AccountHolderId = };
context.Accounts.Add(account);
account = new Account { AccountHolderId = };
context.Accounts.Add(account);
context.SaveChanges();
}
using (var context = new EF6RecipesContext()) {
foreach (var account in context.Accounts) {
Console.WriteLine("Account Id = {0}",
account.AccountHolderId.ToString());
}
}
代码清单2-20,我们使用传统方式,调用在上下文对象的Database属性的方法ExecuteSqlCommand(),向数据库中插入一行数据。之所有这样做,是因为我们需要插入一行DeletedOn列为非null值的数据。在模型中,Account实体没有属性映射此列。实际上,Account实体已不可能被映射到一个有DeletedOn列值的行,而这又恰好是我们需要用来测试的。
第一部分余下的代码,我们创建并初始化了3个Account实体类型的实例,并通过SaveChanges()方法保存到数据加中。
当我们查询数数据库时,我们只能获取到3个通过SaveChanges()方法保存到数据库中的Account实体类型实例。通过方法ExecuteSqlCommand()方法插入的数据将不被显示。下面的输出验证了这个结论:
Account Id = 2320
Account Id = 2502
Account Id = 2603
这篇就到此结束,下篇我们开始TPH.
实体框架交流QQ群: 458326058,欢迎有兴趣的朋友加入一起交流
谢谢大家的持续关注,我的博客地址:http://www.cnblogs.com/VolcanoCloud/
《Entity Framework 6 Recipes》中文翻译系列 (8) -----第二章 实体数据建模基础之继承关系映射TPT的更多相关文章
- 《Entity Framework 6 Recipes》中文翻译系列 (9) -----第二章 实体数据建模基础之继承关系映射TPH
翻译的初衷以及为什么选择<Entity Framework 6 Recipes>来学习,请看本系列开篇 2-10 Table per Hierarchy 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++编辑器和编译器到一个高度集成.支持软件 ...
随机推荐
- iOS---FMDB数据升级
本人在这里重要强调一下!!! 看这里,看这里,看这里,重要的事说三遍. 本人在项目开发中,由于需求问题,不得不对已经建立好的数据库进行修改(添加字段),我就很随意的直接添加了对一个的字段,运行一下,数 ...
- js类数组
类数组 - [ Array-like ] : objects 1. what's Array-like 类数组和数组很像,但是数组的很多方法却不能用,这就很尴尬了~ 像 arguments.nodel ...
- pythony语法小练习
i=[0,1] b=[2,3] print(dict([i,b]))#列表转换为字典 题目:有四个数字:1.2.3.4,能组成多少个互不相同且无重复数字的三位数?各是多少? 程序分析:可填在百位.十位 ...
- iOS 有TabBar的VC界面push后隐藏TabBar的方法
当一个UITabbarController管理多个UINavigationController的时候, 我们要从这每一个UINavigationController中push一个ViewControl ...
- iOS Swift 数组 交换元素的两种方法
swap(&arr[fromIndexPath.row], &arr[to.row]) (arr[fromIndexPath.row],arr[to.row]) = (arr[to.r ...
- java-并发-高级并发对象1
以往说到的线程对象都是java平台中非常初级的API,用于处理一些基本的任务,对于一些复杂高级的工作,就需要一些高级的并发对象,尤其是针对于当今的应用程序,要充分利用现在的多核多处理器系统的性能. 以 ...
- SpringMvc的简单介绍
1.mcv框架要做哪些事情 (a)将url映射到java类或者Java类的方法 (b)封装用户提交的数据 (c)处理请求---调用相关的业务处理,封装响应的数据 (d)将封装的数据进行渲染,jsp,h ...
- js基础
JavaScript组成: ◆ECMASCript 语法标准◆DOM JS操作网页(api) ◆BOM 操作浏览器的api JavaScript特点: ◆简单易用 == ...
- Github+Jekyll —— 创建个人免费博客(一)从零开始
摘要: 本文中我将介绍一下如何在github上搭建个人Blog(博客),也顺便让我们掌握一下github Pages功能,另外还涉及到Jekyll技术. ======================= ...
- linux终端 字符界面 显示乱码
方法一:配置SSH工具 SecureCRT中文版配置 [全局选项]→[默认会话]→[编辑默认设置]→[终端]→[外观]→[字体]→[新宋体 10pt CHINESE_GB2312]→[字符编码 UTF ...