如何用EFCore Lazy Loading实现Entity Split
α角 与 β角
支持 现实生活 的 计算机系统,总有着两大偏差,第一个是 现实生活 与 计算机系统 的α角,另外一个是计算机系统的 逻辑设计 与 物理设计 的β角。举个栗子:
- α角:假设某个公司的商业流程,我们在做计算机自动化的时候,会发生某种程度的改变。可能是用了新计算机系统,需要调整商业流程;也可能是某些商业流程,由于种种原因,没有被计算机系统实现支持。。。
- β角:这个比较常见,例如某个类本身是没有什么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的实现方法。
- DbContext:
用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的更多相关文章
- EFCore Lazy Loading + Inheritance = 干净的数据表 (二) 【献给处女座的DB First程序猿】
前言 本篇是上一篇EFCore Lazy Loading + Inheritance = 干净的数据表 (一) [献给处女座的DB First程序猿] 前菜 的续篇.这一篇才是真的为处女座的DB Fi ...
- EFCore Lazy Loading + Inheritance = 干净的数据表 (一) 【献给处女座的DB First程序猿】
前言 α角 与 β角 关于α角 与 β角的介绍,请见上文 如何用EFCore Lazy Loading实现Entity Split. 本篇会继续有关于β角的彩蛋在等着大家去发掘./斜眼笑 其他 本篇的 ...
- Entity Framework加载相关实体——延迟加载Lazy Loading、贪婪加载Eager Loading、显示加载Explicit Loading
Entity Framework提供了三种加载相关实体的方法:Lazy Loading,Eager Loading和Explicit Loading.首先我们先来看一下MSDN对三种加载实体方法的定义 ...
- Entity Framework Tutorial Basics(37):Lazy Loading
Lazy Loading: One of the important functions of Entity Framework is lazy loading. Lazy loading means ...
- iOS swift lazy loading
Why bother lazy loading and purging pages, you ask? Well, in this example, it won't matter too much ...
- Lazy<T>在Entity Framework中的性能优化实践
Lazy<T>在Entity Framework中的性能优化实践(附源码) 2013-10-27 18:12 by JustRun, 328 阅读, 4 评论, 收藏, 编辑 在使用EF的 ...
- 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 ...
- WPF/SL: lazy loading TreeView
Posted on January 25, 2012 by Matthieu MEZIL 01/26/2012: Code update Imagine the following scenario: ...
- 你所不知道的库存超限做法 服务器一般达到多少qps比较好[转] JAVA格物致知基础篇:你所不知道的返回码 深入了解EntityFramework Core 2.1延迟加载(Lazy Loading) EntityFramework 6.x和EntityFramework Core关系映射中导航属性必须是public? 藏在正则表达式里的陷阱 两道面试题,带你解析Java类加载机制
你所不知道的库存超限做法 在互联网企业中,限购的做法,多种多样,有的别出心裁,有的因循守旧,但是种种做法皆想达到的目的,无外乎几种,商品卖的完,系统抗的住,库存不超限.虽然短短数语,却有着说不完,道不 ...
随机推荐
- office 2019 下载地址
office2019激活密钥 W8W6K-3N7KK-PXB9H-8TD8W-BWTH9 Office2019下载地址: 下载地址 专业增强版(强烈推荐): http://officecdn.m ...
- Python3学习笔记----生成器
生成器的作用 生成器可以在循环的过程中推算出后续的元素,不必创建完整的list,从而节省大量空间. 生成器的创建 1.把一个列表生成式的 [ ] 改写为( ) # -*- coding: UTF-8 ...
- HDFS详解
HDFS详解大纲 Hadoop HDFS 分布式文件系统DFS简介 HDFS的系统组成介绍 HDFS的组成部分详解 副本存放策略及路由规则 命令行接口 Java接口 客户端与HDFS的数据流讲解 目标 ...
- DAY16 模块和包的导入
一.包 1.包的定义 包:就是一系列模块的结合体 2.重点 1.包中一定要有一个专门来管理包中所有模块的文件 2.包名:存放一系列模块的文件夹名字 3.包名(包对象)存放的是管理模块的那个文件的地址, ...
- #宽带选择# V2EX讨论
毫无疑问上海联通是最好的,如果你的小区有的话IP 基本上固定,只要你的路由器不掉线不断电我的 IP 已经 hold 了三个多月了.无论是北美 还是日韩新,联通(上海)出口都甩电信普通家宽 N 条街.如 ...
- Java核心技术卷一 · 笔记(1)
目录 1.java的关键术语 2.==和equals 3.空串与 Null 串 4.构建字符串 5.封装 6.对象的三个主要特性 7.依赖(dependence).聚合(aggregation).继承 ...
- myeclipse连接mysql生成数据表时中文字符乱码或问号(解决方法)
出现这个问题有以下三步解决思路: 1. 检查myeclipse的编码格式 windows---->Preferences---->general---->Workspace,右侧窗口 ...
- SVN-Failed to run the WC DB work queue associated with
解决方法:清空svn的队列 1.下载sqlite3.exe 2.找到你项目的.svn文件,查看是否存在wc.db 3.将sqlite3.exe放到.svn的同级目录 4.启动cmd执行sqlite3 ...
- f-stack nginx多进程报错 primary worker process failed to initialize
EAL: Detected 4 lcore(s)EAL: Detected 1 NUMA nodesEAL: Multi-process socket /var/run/dpdk/rte/mp_soc ...
- 【C/C++】C++11 Lambda
Lambda C++11 中 lambda 是一个匿名函数对象 最简形式 []{ cout << "lambda" << endl; }(); // pri ...