EF6学习笔记五:继承三策略

萝らか妹 提交于 2020-05-03 15:15:02

要专业系统地学习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,但按照我学的这些东西来看,这个不叫继承

TPH

代码一贴你就能知道怎么回事

基类BaseEntity

public class BaseEntity
    {
        public string Id { get; set; }
        public DateTime AddTime { get; set; }
    }
View Code

 

学生类

public class Student:BaseEntity
    {
        public string Name { get; set; }
        public string Number { get; set; }
    }
View Code

 

老师类

public class Teacher:BaseEntity
    {
        public string Name { get; set; }
        public decimal Salary { get; set; }
    }
View Code

 

注意:在上下文中我们必须要对基类公开一个DbSet<>属性

public DbSet<BaseEntity> BaseEntities { get; set; }
View Code

 

生成表结构如下

就是这样,两个子类的属性全部在基类中,而且系统新添加一个字段“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);
            }
View Code

 

我们可以对这种方式的继承再做一点配置,换一种方式来代替“Discriminator”,也没太大变化,只不过分别弄出两个字段,来辨别两个实体

modelBuilder.Entity<BaseEntity>().Map<Student>(m =>
            {
                m.Requires("StudentType").HasValue(1);
            }).Map<Teacher>(m =>
            {
                m.Requires("TeacherType").HasValue(2);
            });
View Code

 

 然后我添加一个学生,一个老师,看看表里面是什么情况

TPH就是这样的,我也不知道这种什么情况下使用,往下面看

TPT

基类Details

public class Details
    {
        public string DetailsId { get; set; }
        public string Decirtions { get; set; }
    }
View Code

 

图书类

public class Book : Details
    {
        public string BookId { get; set; }
        public string Name { get; set; }
        public string Number { get; set; }
    }
View Code

 

水果类

public class Fruit:Details
    {
        public string FruitId { get; set; }
        public string Name { get; set; }
        public decimal Price { get; set; }
    }
View Code

 

然后配置的时候,为这三个model公开Dbset<>属性,并且子类需要在onModelCreating中配置一下,不然就直接映射成TPH模式了

public DbSet<Details> Details { get; set; }
        public DbSet<Book> Books { get; set; }
        public DbSet<Fruit> Fruit { get; set; }
View Code
modelBuilder.Entity<Details>().ToTable("tb_Details");
            modelBuilder.Entity<Book>().ToTable("tb_Books");
            modelBuilder.Entity<Fruit>().ToTable("tb_Fruits");
View Code

 

生成的表结构如下

我添加一个水果,他会默认在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]
View Code

我们查询基类,生成的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]
View Code

 

 TPT的缺点就在于性能差,当然你不能什么都用这样方式,每种方式都有自己特有的使用情境

TPC

 基类

public class Base
    {
        public string Id { get; set; }
        public string Dd { get; set; }
    }
View Code

 

 子类1

public class Child1:Base
    {
        public string Name { get; set; }
        public string Ee { get; set; }
    }
View Code

 

子类2

public class Child2:Base
    {
        public string Name { get; set; }
        public string Dcv { get; set; }
    }
View Code

 

配置

//  TPC
            modelBuilder.Entity<Child1>().Map(m =>
            {
                m.MapInheritedProperties();
                m.ToTable("tb_Child1s");
            });
            modelBuilder.Entity<Child2>().Map(m =>
            {
                m.MapInheritedProperties();
                m.ToTable("tb_Child2s");
            });
View Code

 

 生成的表结构如下

这三种继承策略对我来说,我觉得不用不到,现在也有些乱

最后引用作者的一段话做个总结

“对于单一适用所有场景的映射继承策略不存在,上述每种策略都有其优缺点。如果不需要多表关联或查询,从不或者很少查询基类并且没有与基类关联的类,推荐使用TPC;

如果需要多表关联或查询,并且子类中有较少的属性(特别是子类之间需要进行区别),推荐使用TPH(TPH实现推荐使用自定义Disciminator);

如果需要多表关联或查询,并且子类声明许多属性(子类主要取决于它们所持有的数据),推荐使用TPT。”

 

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!