咱们初始化数据库一节已经知道:EF为每个具体的类生成了数据库的表。如今有了一个问题:咱们在设计领域类时常常用到继承,这能让咱们的代码更简洁且容易管理,在面向对象中有“has a”和“is a”关系(如student has a name,student is a person--继承),然而数据库中只有“has a”关系。数据库管理系统并不支持继承,因此咱们怎么去映射具备继承关系的领域类呢?数据库
EF CodeFirst中有三种方式表示继承体系:app
1.TPH(table per hierarchy): 这是EF的默认方式,这种方式把整个继承体系都映射在一个表中,经过一个discriminator(鉴别器)列来判断继承关系。如Student继承于Person类,那么Student和Person都映射在一张表上,表中有一个discriminator列,这个列帮咱们判断表中的记录示Student仍是Personide
2.TPT(table per type):为每个领域类都建立一张单独的表函数
3.TPC(table per concrete class):为一个具体类建立一张表,抽象类不建立表。这种模式下,若是多个类都继承于一个抽象类,那么每一个具体类都会包含抽象类的属性。性能
咱们先介绍TPH,首先添加领域类和上下文,注意上下文中我只设置了DbSet<BillingDetail>属性,没有为实现类(如BankAccount添加DbSet属性),如图ui
//抽象帐单类 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函数中代码以下:spa
class Program { static void Main(string[] args) { using (InheritanceMappingContext context=new InheritanceMappingContext()) { context.BillingDetails.Add(new BankAccount() {BillingDetailId=1,BankName="建设银行" }); context.SaveChanges(); } } }
运行程序能够看到结果以下所示:数据中建的表名对应父类名的复数: BillingDetails,一张表中包含了父类和子类的每全部属性。同时还有一个discriminator列,列的值是具体类的类名,本例中类名为BankAccount,这一列用于表示记录属于哪个领域类。设计
当咱们执行查询第一条帐单时,返回的类型是抽象类,可是内部是BankAccount类型,没有信用卡类型的字段,以下图:code
这时有一个问题,咱们进行查询时会把全部的子类都查询出来,有没有办法只查询一种具体类型,如只查询信用卡帐单的记录?经过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")); }