要专业系统地学习EF前往《你必须掌握的Entity Framework 6.x与Core 2.0》这本书的作者(汪鹏,Jeffcky)的博客:https://www.cnblogs.com/CreateMyself/
Table Per Hierarchy(TPH)
Table Per Type(TPT)
Table Per Concrete class(TPC)
我弄一下一,发现这些东西我几乎没用过上几篇我写了BaseEntity,但按照我学的这些东西来看,这个不叫继承
代码一贴你就能知道怎么回事
基类BaseEntity
public class BaseEntity { public string Id { get; set; } public DateTime AddTime { get; set; } }
学生类
public class Student:BaseEntity { public string Name { get; set; } public string Number { get; set; } }
老师类
public class Teacher:BaseEntity { public string Name { get; set; } public decimal Salary { get; set; } }
注意:在上下文中我们必须要对基类公开一个DbSet<>属性
public DbSet<BaseEntity> BaseEntities { get; set; }
生成表结构如下
就是这样,两个子类的属性全部在基类中,而且系统新添加一个字段“Discriminator”来区分不同的子类,而且属于子类的属性必须是非空
我们看一下插入、查询数据怎么做
using (EFDbContext db = new EFDbContext()) { // 添加一个学生 db.BaseEntities.Add(new Student { Id = Guid.NewGuid().ToString(), AddTime = DateTime.Now, Name = "张三", Number = "number001" }); db.SaveChanges(); // 查询所有学生,用ofType方法 var res = JsonConvert.SerializeObject(db.BaseEntities.OfType<Student>().ToList()); Console.WriteLine(res); }
我们可以对这种方式的继承再做一点配置,换一种方式来代替“Discriminator”,也没太大变化,只不过分别弄出两个字段,来辨别两个实体
modelBuilder.Entity<BaseEntity>().Map<Student>(m => { m.Requires("StudentType").HasValue(1); }).Map<Teacher>(m => { m.Requires("TeacherType").HasValue(2); });
然后我添加一个学生,一个老师,看看表里面是什么情况
TPH就是这样的,我也不知道这种什么情况下使用,往下面看
基类Details
public class Details { public string DetailsId { get; set; } public string Decirtions { get; set; } }
图书类
public class Book : Details { public string BookId { get; set; } public string Name { get; set; } public string Number { get; set; } }
水果类
public class Fruit:Details { public string FruitId { get; set; } public string Name { get; set; } public decimal Price { get; set; } }
然后配置的时候,为这三个model公开Dbset<>属性,并且子类需要在onModelCreating中配置一下,不然就直接映射成TPH模式了
public DbSet<Details> Details { get; set; } public DbSet<Book> Books { get; set; } public DbSet<Fruit> Fruit { get; set; }
modelBuilder.Entity<Details>().ToTable("tb_Details"); modelBuilder.Entity<Book>().ToTable("tb_Books"); modelBuilder.Entity<Fruit>().ToTable("tb_Fruits");
生成的表结构如下
我添加一个水果,他会默认在details表中添加一条记录
作者说这种方式用的最多,但是性能不是很好
比如我们查询所有的水果,生成的SQL如下
SELECT ‘0X0X‘ AS [C1], [Extent1].[DetailsId] AS [DetailsId], [Extent1].[Decirtions] AS [Decirtions], [Extent2].[FruitId] AS [FruitId], [Extent2].[Name] AS [Name], [Extent2].[Price] AS [Price] FROM [dbo].[tb_Details] AS [Extent1] INNER JOIN [dbo].[tb_Fruits] AS [Extent2] ON [Extent1].[DetailsId] = [Extent2].[DetailsId]
我们查询基类,生成的SQL是这样的,他会连接查询所有的子表
SELECT CASE WHEN (( NOT (([Project2].[C1] = 1) AND ([Project2].[C1] IS NOT NULL))) AND ( NOT (([Project1].[C1] = 1) AND ([Project1].[C1] IS NOT NULL)))) THEN ‘0X‘ WHEN (([Project2].[C1] = 1) AND ([Project2].[C1] IS NOT NULL)) THEN ‘0X0X‘ ELSE ‘0X1X‘ END AS [C1], [Extent1].[DetailsId] AS [DetailsId], [Extent1].[Decirtions] AS [Decirtions], CASE WHEN (( NOT (([Project2].[C1] = 1) AND ([Project2].[C1] IS NOT NULL))) AND ( NOT (([Project1].[C1] = 1) AND ([Project1].[C1] IS NOT NULL)))) THEN CAST(NULL AS varchar(1)) WHEN (([Project2].[C1] = 1) AND ([Project2].[C1] IS NOT NULL)) THEN [Project2].[BookId] END AS [C2], CASE WHEN (( NOT (([Project2].[C1] = 1) AND ([Project2].[C1] IS NOT NULL))) AND ( NOT (([Project1].[C1] = 1) AND ([Project1].[C1] IS NOT NULL)))) THEN CAST(NULL AS varchar(1)) WHEN (([Project2].[C1] = 1) AND ([Project2].[C1] IS NOT NULL)) THEN [Project2].[Name] END AS [C3], CASE WHEN (( NOT (([Project2].[C1] = 1) AND ([Project2].[C1] IS NOT NULL))) AND ( NOT (([Project1].[C1] = 1) AND ([Project1].[C1] IS NOT NULL)))) THEN CAST(NULL AS varchar(1)) WHEN (([Project2].[C1] = 1) AND ([Project2].[C1] IS NOT NULL)) THEN [Project2].[Number] END AS [C4], CASE WHEN (( NOT (([Project2].[C1] = 1) AND ([Project2].[C1] IS NOT NULL))) AND ( NOT (([Project1].[C1] = 1) AND ([Project1].[C1] IS NOT NULL)))) THEN CAST(NULL AS varchar(1)) WHEN (([Project2].[C1] = 1) AND ([Project2].[C1] IS NOT NULL)) THEN CAST(NULL AS varchar(1)) ELSE [Project1].[FruitId] END AS [C5], CASE WHEN (( NOT (([Project2].[C1] = 1) AND ([Project2].[C1] IS NOT NULL))) AND ( NOT (([Project1].[C1] = 1) AND ([Project1].[C1] IS NOT NULL)))) THEN CAST(NULL AS varchar(1)) WHEN (([Project2].[C1] = 1) AND ([Project2].[C1] IS NOT NULL)) THEN CAST(NULL AS varchar(1)) ELSE [Project1].[Name] END AS [C6], CASE WHEN (( NOT (([Project2].[C1] = 1) AND ([Project2].[C1] IS NOT NULL))) AND ( NOT (([Project1].[C1] = 1) AND ([Project1].[C1] IS NOT NULL)))) THEN CAST(NULL AS decimal(18,2)) WHEN (([Project2].[C1] = 1) AND ([Project2].[C1] IS NOT NULL)) THEN CAST(NULL AS decimal(18,2)) ELSE [Project1].[Price] END AS [C7] FROM [dbo].[tb_Details] AS [Extent1] LEFT OUTER JOIN (SELECT [Extent2].[DetailsId] AS [DetailsId], [Extent2].[FruitId] AS [FruitId], [Extent2].[Name] AS [Name], [Extent2].[Price] AS [Price], cast(1 as bit) AS [C1] FROM [dbo].[tb_Fruits] AS [Extent2] ) AS [Project1] ON [Extent1].[DetailsId] = [Project1].[DetailsId] LEFT OUTER JOIN (SELECT [Extent3].[DetailsId] AS [DetailsId], [Extent3].[BookId] AS [BookId], [Extent3].[Name] AS [Name], [Extent3].[Number] AS [Number], cast(1 as bit) AS [C1] FROM [dbo].[tb_Books] AS [Extent3] ) AS [Project2] ON [Extent1].[DetailsId] = [Project2].[DetailsId]
TPT的缺点就在于性能差,当然你不能什么都用这样方式,每种方式都有自己特有的使用情境
基类
public class Base { public string Id { get; set; } public string Dd { get; set; } }
子类1
public class Child1:Base { public string Name { get; set; } public string Ee { get; set; } }
子类2
public class Child2:Base { public string Name { get; set; } public string Dcv { get; set; } }
配置
// TPC modelBuilder.Entity<Child1>().Map(m => { m.MapInheritedProperties(); m.ToTable("tb_Child1s"); }); modelBuilder.Entity<Child2>().Map(m => { m.MapInheritedProperties(); m.ToTable("tb_Child2s"); });
生成的表结构如下
这三种继承策略对我来说,我觉得不用不到,现在也有些乱
最后引用作者的一段话做个总结
“对于单一适用所有场景的映射继承策略不存在,上述每种策略都有其优缺点。如果不需要多表关联或查询,从不或者很少查询基类并且没有与基类关联的类,推荐使用TPC;
如果需要多表关联或查询,并且子类中有较少的属性(特别是子类之间需要进行区别),推荐使用TPH(TPH实现推荐使用自定义Disciminator);
如果需要多表关联或查询,并且子类声明许多属性(子类主要取决于它们所持有的数据),推荐使用TPT。”
原文:https://www.cnblogs.com/jinshan-go/p/10264373.html