zoukankan      html  css  js  c++  java
  • EF6学习笔记五:继承三策略

    要专业系统地学习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; }
        }

    学生类

    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就是这样的,我也不知道这种什么情况下使用,往下面看

    TPT

    基类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的缺点就在于性能差,当然你不能什么都用这样方式,每种方式都有自己特有的使用情境

    TPC

     基类

    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。”

  • 相关阅读:
    springboot maven打包插件
    maven打包指定main入口插件
    团队开发环境一致性性要求
    springboot 在idea中实现热部署
    IDEA 2018.1可用License服务(持续更新)
    IDEA打jar包
    3月18号
    3月17号
    3月16号
    3月13号
  • 原文地址:https://www.cnblogs.com/anyihen/p/12818434.html
Copyright © 2011-2022 走看看