EF CodeFirst系列(3)---EF中的继承策略(暂存)
我们初始化数据库一节已经知道:EF为每一个具体的类生成了数据库的表。现在有了一个问题:我们在设计领域类时经常用到继承,这能让我们的代码更简洁且容易管理,在面向对象中有“has a”和“is a”关系(如student has a name,student is a person--继承),然而数据库中只有“has a”关系。数据库管理系统并不支持继承,所以我们怎么去映射具有继承关系的领域类呢?
EF CodeFirst中有三种方式表示继承体系:
1.TPH(table per hierarchy): 这是EF的默认方式,这种方式把整个继承体系都映射在一个表中,通过一个discriminator(鉴别器)列来判断继承关系。如Student继承于Person类,那么Student和Person都映射在一张表上,表中有一个discriminator列,这个列帮我们判断表中的记录示Student还是Person
2.TPT(table per type):为每一个领域类都创建一张单独的表
3.TPC(table per concrete class):为一个具体类创建一张表,抽象类不创建表。这种模式下,如果多个类都继承于一个抽象类,那么每个具体类都会包含抽象类的属性。
1.TPH(table per hierarchy) 每个继承体系映射一张表
我们先介绍TPH,首先添加领域类和上下文,注意上下文中我只设置了DbSet<BillingDetail>属性,没有为实现类(如BankAccount添加DbSet属性),如图

//抽象账单类
public abstract class BillingDetail
{
public int BillingDetailId { get; set; }//账单Id
public string Owner { get; set; }//账单所有者
public string Number { get; set; }//账单编号
}
//银行账单
public class BankAccount:BillingDetail
{
public string BankName { get; set; }//银行名
public string Swift { get; set; }//银行所属组织
}
//信用卡账单
public class CreditCard:BillingDetail
{
public int CardType { get; set; }//信用卡类型
public string ExpiryMonth { get; set; }//到期月份
public string ExpiryYear { get; set; }//到期年份
}
//上下文
public class InheritanceMappingContext:DbContext
{
public DbSet<BillingDetail> BillingDetails { get; set; }
}
main函数中代码如下:
class Program
{
static void Main(string[] args)
{
using (InheritanceMappingContext context=new InheritanceMappingContext())
{
context.BillingDetails.Add(,BankName="建设银行" });
context.SaveChanges();
}
}
}
运行程序可以看到结果如下所示:数据中建的表名对应父类名的复数: BillingDetails,一张表中包含了父类和子类的每所有属性。同时还有一个discriminator列,列的值是具体类的类名,本例中类名为BankAccount,这一列用于表示记录属于哪一个领域类。

当我们执行查询第一条账单时,返回的类型是抽象类,但是内部是BankAccount类型,没有信用卡类型的字段,如下图:

这时有一个问题,我们进行查询时会把所有的子类都查询出来,有没有办法只查询一种具体类型,如只查询信用卡账单的记录?通过OfType<T>可以帮我们解决这个问题:
BillingDetail firstBilling = context.BillingDetails.OfType<CreditCard>().FirstOrDefault();
这种映射继承的策略简单且性能高,同时能很好地表达多态,推荐使用!
注意:
① 在TPH中每个子类特有的属性必须可空,因为其他子类可能没有当前子类的属性。如BankAccount有BankName属性,而CreditCard没有。
② 这种策略违反了第三范式(第三范式:每个属性都和主键直接相关。BankAccount的记录也有CreditCard的CardType属性,但是CardType和BankAccount的主键不直接相关,虽然Discriminator的值能确定那些列属于BankAccount,但是Discriminator不是主键的一部分)
多态下的EF发送给ADO.NET的SQL:
SELECT
[Extent1].[Discriminator] AS [Discriminator],
[Extent1].[BillingDetailId] AS [BillingDetailId],
[Extent1].[Owner] AS [Owner],
[Extent1].[Number] AS [Number],
[Extent1].[BankName] AS [BankName],
[Extent1].[Swift] AS [Swift],
[Extent1].[CardType] AS [CardType],
[Extent1].[ExpiryMonth] AS [ExpiryMonth],
[Extent1].[ExpiryYear] AS [ExpiryYear]
FROM [dbo].[BillingDetails] AS [Extent1]
WHERE [Extent1].[Discriminator] IN ('BankAccount','CreditCard')
非多态下(OfType过滤)下发送给ADO.NET的Sql
SELECT [Extent1].[BillingDetailId] AS [BillingDetailId], [Extent1].[Owner] AS [Owner], [Extent1].[Number] AS [Number], [Extent1].[BankName] AS [BankName], [Extent1].[Swift] AS [Swift] FROM [dbo].[BillingDetails] AS [Extent1] WHERE [Extent1].[Discriminator] = 'BankAccount'
通过FluentApi改变鉴别器类的数据类型和值(这一点可以在介绍完FluentApi后看)
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<BillingDetail>()
.Map<BankAccount>(m => m.Requires("BillingDetailType").HasValue("BA"))
.Map<CreditCard>(m => m.Requires("BillingDetailType").HasValue("CC"));
}
2.TPT(table per type) 每种类型映射一张表
3.TPC(table per concrete class)每个具体类一张表
EF CodeFirst系列(3)---EF中的继承策略(暂存)的更多相关文章
- 7.翻译系列:EF 6中的继承策略(EF 6 Code-First 系列)
原文地址:http://www.entityframeworktutorial.net/code-first/inheritance-strategy-in-code-first.aspx EF 6 ...
- 9.翻译:EF基础系列---使用EF开发的方式有哪些?
原文链接:http://www.entityframeworktutorial.net/choosing-development-approach-with-entity-framework.aspx ...
- EF CodeFirst系列(2)---CodeFirst的数据库初始化
1. CodeFirst的默认约定 1.领域类和数据库架构的映射约定 在介绍数据库的初始化之前我们需要先了解领域类和数据库之间映射的一些约定.在CodeFirst模式中,约定指的是根据领域类(如Stu ...
- 2.简单的Code First例子(EF Code-First系列)
现在假想,我们想要为讴歌学校创建一个应用程序,这个程序需要能够来添加或者更新学生,分数,教师还有课程信息. 代替之前我们的做法:先是创建数据库,现在我们不这么做,我们先来创建领域类,首先我来创建两个简 ...
- 3.Code-First 约定(EF Code-First系列)
前面,我们已经了解了Code-First利用领域类,怎么为我们创建数据库的简单示例.现在我们来学习一下Code-First约定吧. 什么是约定 约定说白了,就是基于一套规矩办事,这里就是基于你定义好的 ...
- EF CodeFirst系列(6)---配置1对1,1对多,多对多关系
这一节介绍EF CodeFirst模式中的1对0/1,1对多,多对多关系的配置,只有梳理清楚实体间的关系,才能进行愉快的开发,因此这节虽然很简单但是还是记录了一下. 1. 1对0/1关系配置 1. 通 ...
- 4.DB Initialization(数据库初始化)[EF Code-First系列]
前面的例子中,我们已经看到了Code-First自动为我们创建数据库的例子. 这里我们将要学习的是,当初始化的时候,Code-First是怎么决定数据库的名字和服务的呢??? 下面的图,解释了这一切! ...
- EF CodeFirst系列(1)---CodeFirst简单入门
1.什么是CodeFirst 从EF4.1开始,EF可以支持CodeFirst开发模式,这种开发模式特别适用于领域驱动设计(Domain Driven Design,大名鼎鼎的DDD).在CodeFi ...
- EF CodeFirst系列(9)---添加初始化数据和数据库迁移策略
1.添加初始化数据(Seed) 我们可以在初始化数据库的过程中给数据库添加一些数据.为了实现初始化数据(seed data)我们必须创建一个自定义的数据库初始化器(DB initializer),并重 ...
随机推荐
- TableML-GUI篇(C# 编译/解析 Excel/CSV工具)
项目情况 本文接上篇TableML Excel编译/解析工具,本文主要介绍GUI工具的使用,及配置项,如果你想了解此工具更加详细的说明,请阅读上篇文章. 项目地址:https://github.com ...
- Dijango学习_01_pycharm创建应用
一.当初在学dijango的时候,网上的教程非常的杂且多,对于؏؏☝ᖗ乛◡乛ᖘ☝؏؏我们这种初入虎门的小白来说有太多误区 (其实是大佬的操作着实对小白不太友好,原谅我个萌新..2333..) 二.pi ...
- HTML5存储技术Storage
前端存储技术localStorage是永久存储sessionStorage是一次会话存储 localStorage只支持string类型的存储 存进去的所有类型, 取出来之后都变成了string. 一 ...
- APACHE SPARK 2.0 API IMPROVEMENTS: RDD, DATAFRAME, DATASET AND SQL
What’s New, What’s Changed and How to get Started. Are you ready for Apache Spark 2.0? If you are ju ...
- 背景图片固定不随页面上下滚动而滚动 ,属性 background-attachment
<div id="testimonials-section" class="text-center"> </div> css: #tes ...
- Oracle 查询表对应的索引
select col.table_owner "table_owner", idx.table_name "table_name", col.index_own ...
- IDEA+快捷键
格式化代码:ctrl+alt+L IDEA快捷键管理:https://blog.csdn.net/h8178/article/details/78328097 (duplicate:为复制上一行)
- uiautomator2 使用Python测试 Android应用
GitHub地址:https://github.com/openatx/uiautomator2 介绍 uiautomator2 是一个可以使用Python对Android设备进行UI自动化的库.其底 ...
- EChars学习之路1
引入echarts.min.js或者使用CDN https://cdn.bootcss.com/echarts/4.2.1-rc1/echarts.min.js 为ECharts准备一个具备大小(宽高 ...
- HTTP状态码表
HTTP状态码(英语:HTTP Status Code)是用以表示网页服务器HTTP响应状态的3位数字代码.所有状态码的第一个数字代表了响应的五种状态之一. 1xx消息 这一类型的状态码,代表请求已被 ...