α角 与 β角

支持 现实生活 的 计算机系统,总有着两大偏差,第一个是 现实生活计算机系统 的α角,另外一个是计算机系统的 逻辑设计物理设计 的β角。举个栗子:

  • α角:假设某个公司的商业流程,我们在做计算机自动化的时候,会发生某种程度的改变。可能是用了新计算机系统,需要调整商业流程;也可能是某些商业流程,由于种种原因,没有被计算机系统实现支持。。。
  • β角:这个比较常见,例如某个类本身是没有什么ID之类的属性,而由于我们选择了某个数据库产品来做持久化,而数据表的主键用了 某某ID 这样的字段,于是引致我们的 类 里面也可能会包含了 ID 这样的属性;或者由于需要用 SQL Server 的 数据复制 功能,从而使到我们的类加入了各种TimeStamp字段

Entity Split

今天我们讨论的Entity Split,就是属于上述的β角。有时候,由于某些原因(例如 纵向切割 数据表),某个 类 ,它被保存到超过一个的数据表中。例如,我们可能有一个 Customer 类,由于它的属性比较多,于是为了提供系统性能,我们把最常用的属性归纳到 Customers 数据表,而把那些比较少用到的属性归纳到 CustomerOtherInfo 数据表,等等。
在用EF Core的时候,我们会在DbContext.OnModelCreating方法里面用modelBuilder.Entity<MyEntity>().ToTable("Tablename");的做法来指定 BusinessEntity 与 数据表 的映射关系,但是这个只能是Entity级别的,而没有能去到 属性 级别啊 。如何才能做得到指定 “某Entity的某些属性,映射到数据表A;而某些其他属性,映射到数据表B”,这样的效果呢?
(本篇的程序,可以在 https://github.com/kentliu2007/EFCoreDemo/tree/master/EntitySplit 上下载,我用的是 VS2017。建议可以下载之后,对照着程序来阅读本篇)

数据表

先来看看数据表是怎样的:

  • Clients 表的索引

  • ClientContactInfo 表的索引
  • 外键FK_ClientContactInfo_Clients的设置

如何用EF5/6实现 Entity Split

首先让我们先来看看 EF5/6 是怎么实现的。
如果用EF5/6的话,这个很简单。因为有设计器啊,TableMapping就可以轻松搞定。

  • 项目文件:
  • EF Diagram:
  • UnitTest程序:

    看,程序完全不需要考虑数据是来源于不同的两个数据表。简单吧?

如何用EFCore 实现 Entity Split

用EF Core,没有设计器,怎么搞?其实,就算有设计器,也不能和EF5/6那样的实现方式的。
这里我们需要先请出 EF Core的一个重大功能 Lazy Loading。这个功能从EF Core V2 开始支持。

EF Core Lazy Loading
  • 文档:https://docs.microsoft.com/zh-cn/ef/core/querying/related-data#lazy-loading
  • 它有两种实现方式,
    • 一种是用Microsoft.EntityFrameworkCore.Proxies包,以及调用UseLazyLoadingProxies来启用这个包。并且要求类里面的NavigationProperty需要是public且virtual
    • 另外一种使用 Microsoft.EntityFrameworkCore.Abstractions 包中定义的 ILazyLoader 服务的引用。这个需要类里面做更多的特定代码来支持

上述两种做法各有利弊,所以我们接下来会针对两个做法都分别用一次。用它们来实现 基于 EF Core的Entity Split

可行性分析

通过上面分析的EFCore里面ToTable的做法,我们知道,实际上是真的不可避免地需要有俩 Entity ,这样才可以设置它们分别映射到不同的数据表。然后,因为有了Lazy Loading,我们可以对 Client 这个 主类 ,添加引用 ClientContactInfo 类的相应的几个属性。通过玩弄getter和setter的把戏。让EFCore的Lazy Loading在getter/setter调用到ClientContactInfo的属性的时候,按需装载,这样又可以实现Entity Split,系统性能也得到好处。

用Microsoft.EntityFrameworkCore.Proxies来实现EFCore的Entity Split
  • 项目文件:
  • 程序:
    • DbContext:
    • ClientContactInfo:
    • Client:
    • UnitTest:

      看上面UnitTest的程序,就看出来,我们程序调用Client的时候,完全不需要考虑数据是来源于不同的两个数据表。Entity Split就这样搞定了。
      用Microsoft.EntityFrameworkCore.Proxies的缺点是,我们需要有Client.ClientContactInfo这个NavigationProperty。而且还有另外一个可能的坑(如果你尝试调用Client.ClientContactInfo.GetType()就知道了,这个我们可以以后再特别弄个随笔来吐槽一下)。
      接下来,为了维持OOP的美式咖啡口味,让我们换个Lazy Loading的实现方法。
用Microsoft.EntityFrameworkCore.Abstractions来实现EFCore的Entity Split
  • 项目文件:
  • 程序:
    • DbContext (这个和上面那个例子一样,就不骗篇幅了,大家继续参照上面那个Lazy Loading做法的贴图就好)
    • ClientContactInfo(这个和上面那个例子一样,就不骗篇幅了,大家继续参照上面那个Lazy Loading做法的贴图就好)
    • PocoLoadingExtensions (这个是直接抄微软文档上的,所以我也不骗篇幅,大家直接参阅上述微软文档的内容就好。网页上查找一下PocoLoadingExtensions这个文本就能找到了)
    • Client:

      程序里面用了一个private field来存放 ClientContactInfo的 实例,然后用了一个private的ClientContactInfo的property(通过继续玩弄它的getter/setter的把戏来帮忙提高程序的可维护性)
    • UnitTest(这个和上面那个例子一样,就不骗篇幅了,大家继续参照上面那个Lazy Loading做法的贴图就好)
      用Microsoft.EntityFrameworkCore.Abstractions的缺点是,我们的类里面需要加入一些额外的程序(为了支持ILazyLoader )。但是好处是,Client的public属性里面,再也没有ClientContactInfo这种NavigationProperty了。就真的是毫无痕迹地实现了Entity Split。

结语

怎么样?EF Core真的很棒,对吧?借助Lazy Loading的功能,我们花费了一些周折,如此简单地实现了Entity Split。
当然,我本人还是希望Entity Split这个可以built-in为EF Core的一个基本功能,而不是采取借助Lazy Loading这样的Walk Around做法。也许接下来的第N个版本,它就会实现的。毕竟"面包会有的,牛奶会有的,一切都会有的。" :-P
下一篇,让我们继续讨论,如何借助Lazy Loading,在用EF Core的Inheritance功能的时候,继续保持数据表的清洁(不需要有冗余的字段)。敬请期待噢。 :-D

如何用EFCore Lazy Loading实现Entity Split的更多相关文章

  1. EFCore Lazy Loading + Inheritance = 干净的数据表 (二) 【献给处女座的DB First程序猿】

    前言 本篇是上一篇EFCore Lazy Loading + Inheritance = 干净的数据表 (一) [献给处女座的DB First程序猿] 前菜 的续篇.这一篇才是真的为处女座的DB Fi ...

  2. EFCore Lazy Loading + Inheritance = 干净的数据表 (一) 【献给处女座的DB First程序猿】

    前言 α角 与 β角 关于α角 与 β角的介绍,请见上文 如何用EFCore Lazy Loading实现Entity Split. 本篇会继续有关于β角的彩蛋在等着大家去发掘./斜眼笑 其他 本篇的 ...

  3. Entity Framework加载相关实体——延迟加载Lazy Loading、贪婪加载Eager Loading、显示加载Explicit Loading

    Entity Framework提供了三种加载相关实体的方法:Lazy Loading,Eager Loading和Explicit Loading.首先我们先来看一下MSDN对三种加载实体方法的定义 ...

  4. Entity Framework Tutorial Basics(37):Lazy Loading

    Lazy Loading: One of the important functions of Entity Framework is lazy loading. Lazy loading means ...

  5. iOS swift lazy loading

    Why bother lazy loading and purging pages, you ask? Well, in this example, it won't matter too much ...

  6. Lazy<T>在Entity Framework中的性能优化实践

    Lazy<T>在Entity Framework中的性能优化实践(附源码) 2013-10-27 18:12 by JustRun, 328 阅读, 4 评论, 收藏, 编辑 在使用EF的 ...

  7. Lazy Loading | Explicit Loading | Eager Loading in EntityFramework and EntityFramework.Core

    EntityFramework Eagerly Loading Eager loading is the process whereby a query for one type of entity ...

  8. WPF/SL: lazy loading TreeView

    Posted on January 25, 2012 by Matthieu MEZIL 01/26/2012: Code update Imagine the following scenario: ...

  9. 你所不知道的库存超限做法 服务器一般达到多少qps比较好[转] JAVA格物致知基础篇:你所不知道的返回码 深入了解EntityFramework Core 2.1延迟加载(Lazy Loading) EntityFramework 6.x和EntityFramework Core关系映射中导航属性必须是public? 藏在正则表达式里的陷阱 两道面试题,带你解析Java类加载机制

    你所不知道的库存超限做法 在互联网企业中,限购的做法,多种多样,有的别出心裁,有的因循守旧,但是种种做法皆想达到的目的,无外乎几种,商品卖的完,系统抗的住,库存不超限.虽然短短数语,却有着说不完,道不 ...

随机推荐

  1. fcntl设置FD_CLOEXEC标志作用【转】

    本文转载自:https://blog.csdn.net/ustc_dylan/article/details/6930189 通过fcntl设置FD_CLOEXEC标志有什么用?close on ex ...

  2. leetcode实战

    leetcode记录 两数之和 题目 给定一个整数数组 nums 和一个目标值 target,请你在该数组中找出和为目标值的那 两个 整数,并返回他们的数组下标. 你可以假设每种输入只会对应一个答案. ...

  3. Cent OS & Windows 双系统自定义引导菜单

    系统环境 系统:Windows 10 (64-bit) & Cent OS 7 (64-bit) 引导程序:Grub2 编辑 grub.cfg 为了方便,在终端使用命令 su 输入密码进入超级 ...

  4. [IoC容器Unity]第四回:使用范例

    1.引言 前面几个章节介绍了Unity的基本使用,主要分为程序和配置文件两种方法的使用,可以参考一下链接, [IoC容器Unity]第一回:Unity预览 [IoC容器Unity]第二回:Lifeti ...

  5. js原型和原型链理解到面向对象

    一.js中的两种对象,普通对象和函数对象 var obj1 = {}; var obj2 =new Object(); var obj3 = new obj1(); function fun1(){} ...

  6. vue 打开新窗口

    const {href} = this.$router.resolve({ name: 'foo', query: { bar } }) window.open(href, '_blank')

  7. 关于两栏布局,三栏布局,一级点击三角触发select的onchange事件问题

    首先看这样一个效果:,这个截图来自移动端的列表的一整行,在这个效果当中,存在两个技术点,首先选择祝福卡这个宽度是一定的,右边的部分,宽度随着手机屏幕的宽度而自适应,再一个技术点就是点击最右侧向下箭头, ...

  8. springboot缓存注解——@Cacheable和@CacheConfig

    @Caching :制定多个缓存规则 @Cacheable 和 @CachePut 同时标注时 ,@CachePut导致还是会走方法跟数据库交互 //根据lastName查,并将结果缓存,并且可以用于 ...

  9. vue 双向绑定 数据修改但页面没刷新

    在数据改动的代码后加 this.$forceUpdate(); 若是在某个特定方法中 则将this改为方法中设定的名称

  10. 虚拟机中安装Linux系统

    本教程的运行环境:Windows 10 , 虚拟机 VirtualBox,Ubuntu 16.04 1.准备 下载 VirtualBox 下载OVA镜像64位 http://releases.ubun ...