EF里的继承映射关系TPH、TPT和TPC的讲解以及一些具体的例子

简介: 原文:EF里的继承映射关系TPH、TPT和TPC的讲解以及一些具体的例子本章节讲解EF里的继承映射关系,分为TPH、TPT、TPC。具体: 1.TPH:Table Per Hierarchy 这是EF的默认的继承映射关系:一张表存放基类和子类的所有列,自动生成的discriminator列用来区分基类和子类的数据。
原文: EF里的继承映射关系TPH、TPT和TPC的讲解以及一些具体的例子

本章节讲解EF里的继承映射关系,分为TPH、TPT、TPC。具体:

1.TPH:Table Per Hierarchy

这是EF的默认的继承映射关系:一张表存放基类和子类的所有列,自动生成的discriminator列用来区分基类和子类的数据。新建一个度假村Resort实体类试试:

    /// <summary>
    /// 度假村类
    /// </summary>
    public class Resort : Lodging  //这里继承了Lodging类
    {
        public string Entertainment { get; set; }  //娱乐
        public string Activities { get; set; }  //活动
    }

之前的住宿类Lodging里有个属性IsResort表示是否度假胜地,现在可以注释掉了,有新的类Resort来继承Lodging表示是否是度假胜地了,跑下程序最终会生成一张表:

并没有生成Resorts表,而是把Resrot实体类里的属性生成到了Lodgings表里。多了一列discriminator,这个是默认的,用来表示数据来自哪个类,继续添加一个插入Lodging表数据的方法:

        private static void InsertLodging()
        {
            var lodging = new CodeFirst.Model.Lodging
            {
                Name = "Rainy Day Motel",
                Destination = new CodeFirst.Model.Destination
                {
                    Name = "Seattle, Washington",
                    Country = "USA"
                }
            };
            using (var context = new CodeFirst.DataAccess.BreakAwayContext())
            {
                context.Lodgings.Add(lodging);
                context.SaveChanges();
            }
        }

再添加一个插入Resort表数据的方法:

        private static void InsertResort()
        {
            var resort = new CodeFirst.Model.Resort
            {
                Name = "Top Notch Resort and Spa",
                MilesFromNearestAirport = 30,
                Activities = "Spa, Hiking, Skiing, Ballooning",
                Destination = new CodeFirst.Model.Destination
                {
                    Name = "Stowe, Vermont",
                    Country = "USA"
                }
            };
            using (var context = new CodeFirst.DataAccess.BreakAwayContext())
            {
                context.Lodgings.Add(resort);
                context.SaveChanges();
            }
        }

在Main方法里调用两个插入方法,可得到如下数据:

两个插入的数据都到了一张表里。Discriminator列表示数据来自哪一列。当然是可以配置的,这里就必须使用Fluent API配置了,Data Annotation表示无能为力,到LodgingMap里进行配置:

this.Map<CodeFirst.Model.Lodging>(l => { l.Requires("From").HasValue("Standard"); });
this.Map<CodeFirst.Model.Resort>(l => { l.Requires("From").HasValue("Resort"); });

生成了我们指定的From列,数据Standard、Resort分别表示来自Lodging和Resrot表,形象点就是1号酒店是普通酒店,2号就是是度假胜地的酒店:

当然,这里甚至可以把HasValue方法里的参数设置成True和False,用布尔类型的数据区分普通酒店和度假胜地的酒店更形象,园友lk8167给了一个更形象的普通售货员和销售经理的例子

2.TPT:Table Per Type

父类和子类在不同的表里。使用Data Annotation配置TPT:

[Table("Resorts")]
public class Resort : Lodging
{
  public string Entertainment { get; set; }
  public string Activities { get; set; }
}

或者使用Fluent API配置:

            this.Map(m =>
            {
                m.ToTable("Lodgings");
            }).Map<CodeFirst.Model.Resort>(m =>
            {
                m.ToTable("Resorts");
            });

注意:上面配置TPH的Fluetn API需要注释掉在跑程序,那是测试TPH的配置。同时释放这句的注释:context.Database.Initialize(true);,这里没修改实体,但是也需要重新生成数据库。最终数据库是这样的:

父类和子类实体都有一张表,子表通过主键LodgingId找到父表:

3.TPC:Table Per Concrete Type

为每个子类建立一个表,每个与子类对应的表中包含基类的属性对应的列和子类特有属性对应的列。同样之前配置TPT的Fluent API需要先注释掉,然后我们通过Fluent API配置下TPC,TPC也无法用Data Annotation配置:

         this.Map(m =>
            {
                m.ToTable("Lodgings");
            }).Map<CodeFirst.Model.Resort>(m =>
            {
                m.ToTable("Resorts");
                m.MapInheritedProperties();
            });

 生成的数据库:

 

可见,子类Resorts类也有了基类的所有属性。

注意:为了方便测试生成TPC,我注释了所有Lodging表的导航属性,主要是和Destination的一对多关系、Destination类也需要注释掉Lodging属性和Fluent API关系配置,否则程序跑起来会报DataException错:
An exception occurred while initializing the database. See the InnerException for details.

大家下载demo使用的时候也需要先注释掉Lodging类的导航属性。当然不注释想保留也可以,必须设置外键为可空类型,具体请参考Programming Entity Framework: Code First 第五章 Avoiding Mapping Exceptions with TPC

讲了这几种方式配置继承映射,实际项目中应该用哪个呢?

  1. 不推荐使用TPC(Type Per Concrete Type),因为在TPC方式中子类中包含的其他类的实例或实例集合不能被映射为表之间的关系。你必须通过手动地在类中添加依赖类的主键属性,从而让 Code First感知到它们之间的关系,而这种方式是和使用Code First的初衷相反的;
  2. 从查询性能上来说,TPH会好一些,因为所有的数据都存在一个表中,不需要在数据查询时使用join;
  3. 从存储空间上来说,TPT会好一些,因为使用TPH时所有的列都在一个表中,而表中的记录不可能使用所有的列,于是有很多列的值是null,浪费了很多存储空间;
  4. 从数据验证的角度来说,TPT好一些,因为TPH中很多子类属性对应的列是可为空的,就为数据验证增加了复杂性。

摘自这里,本文源码

本系列文章结束,主要讲解了EF里如何使用Code First的方式配置数据库,基本上都是手写的配置,其实大家可能已经想到会有工具可以自动配置这些关系了,对了,就是EF Power Tools。这个工具相当智能,可以直接配置出所有的关系。不过个人还是建议关系不多的话自己手写Fluent API来配置。

另外,前面配置了那么长时间的一对多、多对多等各种关系。配置好了如何用EF对这些数据进行增查改查呢?后续还会有系列文章讲解EF是如何操作数据库的,请保持关注。

EF Code First 系列文章导航
  1. EF Code First 初体验
  2. EF里的默认映射以及如何使用Data Annotations和Fluent API配置数据库的映射  本节源码
  3. EF里Guid类型数据的自增长、时间戳和复杂类型的用法  本节源码
  4. EF里一对一、一对多、多对多关系的配置和级联删除  本节源码
  5. EF里的继承映射关系TPH、TPT和TPC的讲解以及一些具体的例子  本节源码
目录
相关文章
|
C# 前端开发
WPF DatePicker默认显示当前日期,格式化为年月日
原文:WPF DatePicker默认显示当前日期 WPF的日历选择控件默认为当前日期,共有两种方法,一种静态,一种动态。 静态的当然写在DatePicker控件的属性里了,动态的写在对应的cs文件里,具体请看下面。
6917 0
|
9月前
|
人工智能 移动开发 JavaScript
AI + 低代码技术揭秘(一):概述
VTJ.PRO 是一个基于 AI 的 Vue3 低代码开发平台,支持 Vue 单文件组件(SFC)与领域特定语言(DSL)之间的双向转换。它构建于 monorepo 架构之上,提供同步版本控制和全面的软件包生态系统,涵盖可视化设计、代码生成及多平台部署功能,同时兼容现有 Vue 3 工作流。平台特点包括双向代码流、AI 集成、Vue 3 基础支持、多平台适配以及低学习门槛等。通过模块化架构与智能工具,VTJ 加速开发流程并保持灵活性,适用于 Web、移动及跨平台项目。当前版本为 0.12.40,源码托管于 Gitee。
366 8
AI + 低代码技术揭秘(一):概述
|
机器学习/深度学习 自然语言处理 语音技术
迁移学习(Transfer Learning)
迁移学习是一种机器学习技术,通过将一个任务中学到的知识应用于另一个相关任务,有效解决了数据稀缺和计算资源有限的问题。它涉及预训练模型、特征提取、微调、领域适应等多种技术,广泛应用于计算机视觉、自然语言处理等领域,显著提升了模型的泛化能力和新任务的性能。
|
NoSQL Cloud Native atlas
探索云原生数据库:MongoDB Atlas 的实践与思考
【10月更文挑战第21天】本文探讨了MongoDB Atlas的核心特性、实践应用及对云原生数据库未来的思考。MongoDB Atlas作为MongoDB的云原生版本,提供全球分布式、完全托管、弹性伸缩和安全合规等优势,支持快速部署、数据全球化、自动化运维和灵活定价。文章还讨论了云原生数据库的未来趋势,如架构灵活性、智能化运维和混合云支持,并分享了实施MongoDB Atlas的最佳实践。
|
XML 开发框架 前端开发
利用代码生成工具Database2Sharp生成ABP VNext框架项目代码
利用代码生成工具Database2Sharp生成ABP VNext框架项目代码
|
机器学习/深度学习 存储 前端开发
实战揭秘:如何借助TensorFlow.js的强大力量,轻松将高效能的机器学习模型无缝集成到Web浏览器中,从而打造智能化的前端应用并优化用户体验
【8月更文挑战第31天】将机器学习模型集成到Web应用中,可让用户在浏览器内体验智能化功能。TensorFlow.js作为在客户端浏览器中运行的库,提供了强大支持。本文通过问答形式详细介绍如何使用TensorFlow.js将机器学习模型带入Web浏览器,并通过具体示例代码展示最佳实践。首先,需在HTML文件中引入TensorFlow.js库;接着,可通过加载预训练模型如MobileNet实现图像分类;然后,编写代码处理图像识别并显示结果;此外,还介绍了如何训练自定义模型及优化模型性能的方法,包括模型量化、剪枝和压缩等。
965 1
|
数据处理 开发者 C#
WPF数据绑定实战:从零开始,带你玩转数据与界面同步,让你的应用程序更上一层楼!
【8月更文挑战第31天】在WPF应用开发中,数据绑定是核心技能之一,它能实现界面元素与数据源的同步更新。本文详细介绍了WPF数据绑定的概念与实现方法,包括属性绑定、元素绑定及路径绑定等技术,并通过示例代码展示了如何创建数据绑定。通过数据绑定,开发者不仅能简化代码、提高可维护性,还能提升用户体验。无论初学者还是有经验的开发者,都能从中受益,更好地掌握WPF数据绑定技巧。
402 0
|
人工智能
AI大咖说-如何评价论文的创新性
《AI大咖说》探讨论文创新性,强调新意、有效性和领域研究问题的重要性。创新点在于用新颖方法有效解决研究问题。评价公式:价值=问题大小*有效性*新意度。该观点源于《跟李沐学AI》视频,提供1-100分评分标准,助力评估论文价值。5月更文挑战第14天
561 3
|
存储 XML 缓存
「领域驱动设计」领域驱动的设计和开发最佳实践
「领域驱动设计」领域驱动的设计和开发最佳实践
|
弹性计算 应用服务中间件 nginx
Nginx配置80端口用于多个域名
Nginx配置80端口用于多个域名
1486 0

热门文章

最新文章