Entity Framework Code First 映射继承关系
转载 http://www.th7.cn/Program/net/201301/122153.shtml
Code First如何处理类之间的继承关系。Entity Framework Code First有三种处理类之间继承关系的方法,我们将逐一介绍这三种处理方法。
1.Table Per Hierarchy(TPH): 只建立一个表,把基类和子类中的所有属性都映射为表中的列。
2.Table Per Type(TPT): 为基类和每个子类建立一个表,每个与子类对应的表中只包含子类特有的属性对应的列。
3.Table Per Concrete Type(TPC):为每个子类建立一个表,每个与子类对应的表中包含基类的属性对应的列和子类特有属性对应的列。
1.Table Per Hierarchy(TPH)
在这种处理方式中,Entity Framework Code First为基类和所有子类建立一个表,基类和子类中的所有属性都映射为表中的一个列。Entity Framework Code First默认在这个表中建立一个叫做Discriminator的列,类型是nvarchar,长度是128。Entity Framework Code First会在存储基类或子类的时候,把类名作为Discriminator列的值。
在我们前面的示例程序中,由于我们要记录订单是被谁创建的,以及是被谁批准的,我们新增了一个SalesPerson类。
public class SalesPerson {
public string EmployeeID { get; set; }
public string Name { get; set; }
public string Gender { get; set; }
public DateTime HiredDate { get; set; }
}
并且在Order类中增加了两个SalesPerson的实例用于记录订单的创建人和批准人。
public SalesPerson CreatedBy { get; set; }public SalesPerson ApprovedBy { get; set; }
我们后来细化了我们的业务流程:订单是由销售员创建的;当客户要求的订单折扣过高时,需要销售经理的审批;经理每个月都有固定的折扣审批总额。销售员和销售经理都属于销售人员。这是一个典型的继承关系。

根据我们细化之后的业务流程,我们创建了两个新的类, SalesMan和SalesManager
public class SalesMan : SalesPerson {
public decimal DiscountLimit { get; set; }
}
public class SalesManager : SalesPerson {
public decimal DiscountAmountPerMonth { get; set; }
}
由于创建订单的时候涉及到了复杂的业务逻辑,需要为订单指定Customer和SalesMan, 我们新建了一个factory类用于创建订单。
public static class OrderFactory{
public static Order CreateNewOrder(Customer customer, SalesMan createUser) { Order order = new Order();
order.Customer = customer;
order.CreatedDate = DateTime.Now;
order.CreatedBy = createUser;
order.ApprovedBy = null;
return order; }
}
我们新建一个单元测试方法用于测试我们新的销售人员继承关系以及新的订单factory类。
[TestMethod]
public void CanAddOrderWithSalesMan() {
OrderSystemContext unitOfWork = new OrderSystemContext(); ProductRepository productRepository = new ProductRepository(unitOfWork); OrderRepository orderRepository = new OrderRepository(unitOfWork); CustomerRepository customerRepository = new CustomerRepository(unitOfWork); SalesMan salesman = new SalesMan {
EmployeeID = "2012001",
Gender = "M",
Name = "Eric",
HiredDate = DateTime.Parse("2010-5-19") };
Customer customer = customerRepository.GetCustomerById("120104198403082113");
Order order = OrderFactory.CreateNewOrder(customer, salesman); order.AddNewOrderItem(productRepository.GetProductCatalogById(1).GetProductInStock(), 5100);
orderRepository.AddNewOrder(order);
unitOfWork.CommitChanges(); }
执行完我们的测试程序之后,我们可以打开SQL Server去看一下Code First默认情况下是如何处理类之间的继承关系的。

Code First默认会把基类和子类的所有属性都映射成一个表中的列,并且会增加一个Discriminator列标识存进去的是哪个类的实例。

如果你不喜欢Discriminator这个有点奇怪的名字,你可以自己定义Discriminator列的名字以及它的类型。我们使用map方法定义该列的名字和类型。我们可以将它命名为Title。
public class SalesPersonValueObjectConfiguration: EntityTypeConfiguration<SalesPerson> {
public SalesPersonValueObjectConfiguration()
{ HasKey(p => p.EmployeeID).Property(p => p.EmployeeID).HasDatabaseGeneratedOption(DatabaseGeneratedOption.None); Property(p => p.Name).IsRequired().HasMaxLength(100);
Property(p => p.Gender).IsRequired().HasMaxLength(1);
Map<SalesMan>(salesman => { salesman.Requires("Title").HasValue("SalesMan"); });
Map<SalesManager>(manager => { manager.Requires("Title").HasValue("Sales Manager"); }); } }
Map方法中传入的类型参数是子类的类名,Requires用于指定Discriminator列的名字,HasValue用于指定它的类型和每个子类对应的值。
我们可以重新执行我们的测试程序,然后打开SQL Server,去看一下新建的数据库表结构。


这个列的类型不仅可以是字符串,还可以是bit标志位,比如说我们把区分salesman和salemanager的列设为bit型,列的名字叫做IsManager.
Map<SalesMan>(salesman => { salesman.Requires("IsManager").HasValue(false); });
Map<SalesManager>(manager => { manager.Requires("IsManager").HasValue(true); });
我们只需要把HasValue中传入的值变为true和false,Code First会自动把IsManager列的类型设置为bit。


2.Table Per Type(TPT)
在这种处理方式中,Entity Framework Code First会为每个基类和子类建立一个表,子类的表中只包含子类特有的属性。
我们可以使用Map方法强制让Code First使用TPT方式,因为Code First默认使用的是TPC方式。
public class SalesPersonValueObjectConfiguration: EntityTypeConfiguration<SalesPerson> { public SalesPersonValueObjectConfiguration() { HasKey(p => p.EmployeeID).Property(p => p.EmployeeID).HasDatabaseGeneratedOption(DatabaseGeneratedOption.None);
Property(p => p.Name).IsRequired().HasMaxLength(100);
Property(p => p.Gender).IsRequired().HasMaxLength(1);
Map<SalesMan>(salesman => { salesman.ToTable("SalesMan"); }); Map<SalesManager>(manager => { manager.ToTable("Manager"); }); }
}
我们通过使用ToTable方法,让Code First为每个子类型建立一个表,表的名字就是ToTable方法中传入的参数值,子类对应的表中的主键与基类对应的表中的主键名字相同,同时它还是指向基类对应的表的外键。
我们还使用上面的那个测试方法来测试一下Code First按照TPT的方式建立的数据表结构。

3.Table Per Concrete Type(TPC)
在这种处理方式中,Entity Framework Code First为每一个子类建立一个表,在子类对应的表中除了子类特有的属性外还有基类的属性对应的表。
和TPT一样,我们也需要通过Map方法进行设置。
public class SalesPersonValueObjectConfiguration: EntityTypeConfiguration<SalesPerson> {
public SalesPersonValueObjectConfiguration() {
HasKey(p => p.EmployeeID).Property(p => p.EmployeeID).HasDatabaseGeneratedOption(DatabaseGeneratedOption.None);
Property(p => p.Name).IsRequired().HasMaxLength(100);
Property(p => p.Gender).IsRequired().HasMaxLength(1);
Map<SalesMan>(salesman => { salesman.ToTable("SalesMan");
salesman.MapInheritedProperties(); });
Map<SalesManager>(manager => { manager.ToTable("Manager"); manager.MapInheritedProperties(); }); } }
通过MapInheritedProperties方法就可以强制Code First使用TPC方式。
我们重新编译之后执行我们原来的测试方法,可以得到不同的数据表结构,Code First不会为基类建立表,而是为每个子类都建立一个表,将子类的内容和基类的内容都存储到各个子类对应的表中。

PS:如果你的基类是abstract,效果也是一样的。
最后需要探讨的一个问题是我们在实际项目中应该使用哪种方式呢?
1.不推荐使用TPC(Type Per Concrete Type),因为在TPC方式中子类中包含的其他类的实例或实例集合不能被映射为表之间的关系。你必须通过手动地在类中添加依赖类的主键属性,从而让Code First感知到它们之间的关系,而这种方式是和使用Code First的初衷相反的。
2.从查询性能上来说,TPH会好一些,因为所有的数据都存在一个表中,不需要在数据查询时使用join。
3.从存储空间上来说,TPT会好一些,因为使用TPH时所有的列都在一个表中,而表中的记录不可能使用所有的列,于是有很多列的值是null,浪费了很多存储空间。
4.从数据验证的角度来说,TPT好一些,因为TPH中很多子类属性对应的列是可为空的,就为数据验证增加了复杂性。
Entity Framework Code First 映射继承关系的更多相关文章
- Entity Framework Code First关系映射约定
本篇随笔目录: 1.外键列名默认约定 2.一对多关系 3.一对一关系 4.多对多关系 5.一对多自反关系 6.多对多自反关系 在关系数据库中,不同表之间往往不是全部都单独存在,而是相互存在关联的.两个 ...
- Entity Framework Code First主外键关系映射约定
本篇随笔目录: 1.外键列名默认约定 2.一对多关系 3.一对一关系 4.多对多关系 5.一对多自反关系 6.多对多自反关系 在关系数据库中,不同表之间往往不是全部都单独存在,而是相互存在关联的.两个 ...
- Entity Framework Code First关系映射约定【l转发】
本篇随笔目录: 1.外键列名默认约定 2.一对多关系 3.一对一关系 4.多对多关系 5.一对多自反关系 6.多对多自反关系 在关系数据库中,不同表之间往往不是全部都单独存在,而是相互存在关联的.两个 ...
- Entity Framework Code First属性映射约定
Entity Framework Code First与数据表之间的映射方式有两种实现:Data Annotation和Fluent API.本文中采用创建Product类为例来说明tity Fram ...
- 补习知识:Entity Framework Code First属性映射约定
Entity Framework Code First与数据表之间的映射方式有两种实现:Data Annotation和Fluent API.本文中采用创建Product类为例来说明tity Fram ...
- Entity Framework Code First属性映射约定 转载https://www.cnblogs.com/libingql/p/3352058.html
Entity Framework Code First属性映射约定 Entity Framework Code First与数据表之间的映射方式有两种实现:Data Annotation和Flue ...
- MVC使用Entity Framework Code First,用漂亮表格显示1对多关系
部门和职员是1对多关系.用一个表格列出所有部门,并且在每行显示该部门下的所有职员名称.如下: 部门和职员的Model: using System.Collections.Generic; namesp ...
- 使用 Entity Framework Code First
使用 Entity Framework Code First 在家闲着也是闲着,继续写我的[ASP.NET MVC 小牛之路]系列吧.在该系列的上一篇博文中,在显示书本信息列表的时候,我们是在程序代码 ...
- Entity Framework Code first(转载)
一.Entity Framework Code first(代码优先)使用过程 1.1Entity Framework 代码优先简介 不得不提Entity Framework Code First这个 ...
随机推荐
- android106 C基本数据类型
#JNI java native interface #c的基本数据类型 * int:32位,能表示的数字是2的32次方个 * 最高位用来表示符号位,那么还剩下31位可以表示数值,所以能表示的数字就是 ...
- c语言学习
#include <stdio.h>void sum_diff(int *n1,int *n2,int *sum,int *diff){ *sum=*n1+*n2; *diff=(*n1& ...
- Cookie中图片的浏览记录与cookie读取servle时路径的设置(文字描述)
public class ShowServlet extends HttpServlet { public void doGet(HttpServletRequest request, HttpSer ...
- Scala函数字面量
Scala中函数为头等公民,你不仅可以定义一个函数然后调用它,而且你可以写一个未命名的函数字面量,然后可以把它当成一个值传递到其它函数或是赋值给其它变量.下面的例子为一个简单的函数字面量(参考整数字面 ...
- Bootstrap--组件之下拉菜单
用于显示链接列表的可切换.有上下文的菜单. 对齐 B默认情况下,下拉菜单自动沿着父元素的上沿和左侧被定位为 100% 宽度. 为 .dropdown-menu 添加 .dropdown-menu-ri ...
- nofollow标签如何使用
“nofollow”的意思是不传递权重,向网站站长提供了一种方式,即告诉搜索引擎“不要追踪此网页上的链接”或“不要追踪此特定链接”. nofllow的形式 1.<meta name=" ...
- Dom操作--全选反选
我们经常会在网站上遇到一些多选的情况,下面我就来说说使用Dom写全选反选的思路. 全选思路:首先,我们来分析一下知道,当我们点击"全选"复选框的时候,所有的复选框应该都被选中,那我 ...
- poj1417 true liars(并查集 + DP)详解
这个题做了两天了.首先用并查集分类是明白的, 不过判断是否情况唯一刚开始用的是搜索.总是超时. 后来看别人的结题报告, 才恍然大悟判断唯一得用DP. 题目大意: 一共有p1+p2个人,分成两组,一组p ...
- c语言学习之基础知识点介绍(二十):预处理指令
一.预处理指令的介绍 预处理命令:在编译之前触发的一系列操作(命令)就叫预处理命令. 特点:以#开头,不要加分号. #include: 文件包含指令 把指定文件的内容复制到相应的位置 #define: ...
- 转载:在Visual Studio 2013中管理中国特色的社会主义Windows Azure
原文链接: http://www.pstips.net/get-azurechinacloud-settings.html 谷歌被豪迈地放弃了中国市场,微软仍旧在中国市场摸爬滚打,跪着挣钱.其中私人定 ...