NET主流ORM框架分析

接上文我们测试了各个ORM框架的性能,大家可以很直观的看到各个ORM框架与原生的ADO.NET在境删改查的性能差异。这里和大家分享下我对ORM框架的理解及一些使用经验。

    ORM框架工作原理

    所有的ORM框架的工作原理都离不开下面这张图,只是每个框架的实现程度不同但是最终的目的是相同的。

    如果是一个ORM框架那么一定会有上图中蓝色部分的这几个元素,无论是增删改查对于ORM一定是以对象为起点,使用对象构造出LINQ表达式,这样我们在对象的世界中可以描述我们希望对数据库所进行的操作,LINQ的最终实现其实也是Lambda表达式(必尽LINQ在代码上会直观很多),功能较强的ORM中都会记录有对象类型到数据库对象的元数据,使用这些元数据可以将复杂的Lambda表达式翻译成一个通用的中间表达式,这个表达式其实是抽象于各个不同数据库的具体实现,最后中间表达式再按指定数据库的具体实现生成最终的SQL语句,交由ADO.NET对象执行到数据库,如果数据存在返回则会回写到CLR对象中。

    以上概括了所有ORM的实现过程,这里比较典型的是Dapper,它之所以小巧而高性能的原因正是因为该框架中没有绿色的部分,可以说是所有ORM框架中最为精简的(可能还会存在其他类似的框架,这里仅用Dapper作为典型案例说明),因此没有多余的部分自然小而快。然而绿色部分的存在就决定了该ORM框架的功能到底有多强,刚好目前世面上的ORM框架中只有EF是完全实现了绿色框中的各个元素,并且支持最为全面,所以EF必然重而慢(至少EF6相对其他框架是比较差的)。

    典型ORM框架实现

    1.  Dapper:需要自己手写SQL语句完成操作,比较简单直接不过还有DapperExtensions的帮助,可以在插入、修改及删除时不用写SQL语句全对象操作,这里需要指出的是它应该不能算是一个完全的ORM框架,因为日常开发占比多的查询还是需要手写SQL,无法对象类型化。

    2. Nhibernate:这是个比较久远的框架,该框架需要自己配置元数据功能上会比DapperExtensions强一点,不过对于查询表达式没有太好的支持。

    3. SqlSugar,Chloe.ORM:此类框架虽然不能支持LINQ但是可以实现自己一套Lambda表达式用于生成SQL语句,总之其总体思想就是把SQL语法转换成表达式语法,比较典型的是会用表达式中的变量名用作生成SQL语句中的别名,而且附加功能会比较多,总之比较灵活实用。

    4. Linq2db:此类ORM算是实现的比较完整的ORM流程(见上图),而且支持众多的数据库,总之功能算是比较强大了。

    EF功能最强的ORM

     EF是目前为止功能最强的ORM,这个相信大家没有什么争议,大家可以参考一下这份文档 (EF Core and EF6 Feature by Feature Comparison),其中列出了EF6与EFCore的功能特性,相信没有哪个ORM框架实现了其中大部分的特性,下面随便列举几个特性:

    • 全面支持LINQ查询,有不少框架也会支持LINQ可是使用时会发现有些写法是不支持的,但是EF是目前支持最全的,在截止到目前为止EFCore的正式版本中也没有完全实现EF6对LINQ的支持。

    • EFCore性能提升:从上一遍博客(Mego(1))中可以看出对于个功能强大的ORM框架而言EFCore已提升相当多了总之已非常接近原生的ADO.NET框架了。

    • 对象继承:这是个很好的设计,它可以让你将关系数据库实现对象继承化,在数据表中也会存在等价父子表。

    • 数据库迁移功能,无论你怎么看待这个功能,但是对于开发而言这个功能太实用了,相信大家在很多DEMO中可以见到使用EF生成数据库。

    • 对象的集合属性展开,通常我们在取订单时也希望一起把明细也取出来,更多的情况是还可以把订单数据分页,这个功能在大部ORM都是没有的需要自己处理,然后在EF中只需要写一行代码就能搞定。

    • 多对多关系:这个也是个比较常用但是也在其他ORM比较少见的功能,EF也是目前支持最好的。

    • EF框架对接了微软的众多其他需要数据访问的框架,例如ASP.NET Identity,ASP.NET WebApi ODATA。

    EF与EFCore缺陷

     虽然EF或EFCore功能已经很强大了,不过在开发过程中还有很多不足的地方,也有很多缺陷这也是导致有很多人使用之后而放弃的原因之一(当然还有EF6的性能问题)。下面将列一下EF6的不足给大家参考。

    数据更新功能不足

    在EF中数据提交的功能一直不行,在EF6中所有的数据操作都是单个对象分别提交了,这也就表示在大量数据提交过程中每个对象都要发送一次数据库,这也是导致为什么性能底下了,而且更新及删除数据一定要从数据库取回对象或附加对象才能操作。这个问题在EFCore中都得到解决而且性能比较好如前文的测试可见。但是EF到目前都不能支持条件更新、条件删除。不过这个问题可以使用这个框架(Entity Framework Plus)解决,不过这个框架是收费的。

    创建时间修改时间问题

     我们在业务系统开发时常会遇到创建和更新数据时需要记录创建时间或修改时间的情况,不过目前无论是EF或EFCore中都只能用触发器来解决,不过当数据表变多时维护这些触发器也是个比较麻烦的事情。有人会想到用默认值,这个EF中是支持的,但是EF不能选择性的插入或更新指定的字段,这是个两难的情况。

    创建及修改时间只是这类问题的一个例子,这类问题可以概括为我们在插入或更新数据时希望指定字段是调用相应的数据表达式生成的,而不是应用程序的值,这里可以举个实例给大家看,在一个负载均衡的架构中我们需要在创建订单时生成订单流水号,这个流水号一定要唯一且连续,在这种多服务器并发的情况下只有在数据库生成这个流水号才是比较好的选择,这样可以充分保证当提交失败时新分的流水号可以归还,同时也保证大家依次生成不会重复且连续,这个时候就很需要ORM可以自动使用单号生成函数来生成值。

    EF中的曲线解决,在EF6中插入、更改或删除操作是支持存储过程映射的,大家可以参考这个文章(Entity Framework Code First Insert, Update, and Delete Stored Procedures (EF6 onwards))里面有详细的说明,这里我们可以做文章,我们修改存储过程改为调用我们指定的表达式即可,这样可以不用触发器,也能达到目的。这里有人会问维护存储也需要比较大代价,这里我们可以重写EF的数据库迁移生成存储过程的代码来统一处理。

    实体集合属性多层展开

     无论是EF或EFCore中有一个Include函数,用来在返回实体对象时显示包含它的集合或对象属性,这个操作被称为属性展开,这里先这么称呼。不过可以展开对象属性(例如订单 -> 客户 -> 负责人 -> 公司),也可以展开集合属性(例如 订单 -> 明细集合),这时集合下面的属性就不能展开了例如我希望得到(例如 订单 -> 明细集合 -> 产品)这是不支持的。

    补充EF实际内部代码是可以支持这种操作但是没公开,如果有对ASP.NET WebApi OData有所了解的朋友可以知道,如果你的OData服务是建立在EF基础之上的,在ODATA语法中可以支持多级数据展开的,并且在EF6中得到很好的实现,不过这些的前提是你的数据库是SQL Server。

    对象关系限制

    例如一个订单会有多个明细,每个明细都会对应一个产品,从逻辑上我们就可以认为订单和产品是多对多关系了,但是在EF中就没有这么自由的可以操作了。

    在EF中关系的操作有很多限制,这个特别在多对多关系中很严重,EF6中会生成一个隐藏的关系实体用来建立关联,这些都是你不能控制的,而且在关系链接中要求主表一定要是主键,不过在EFCore中这些问题得到一些解决,不过EFCore目前不能支持多对多关系,至少目前的Flush API中还没有看到。

    时间戳

    感觉EF中不少特性都是为SQL Server设计的,EF中强制时间戳必须CLR属性是字节数组,不过在MySQL中的时间戳其实就是日期时间。

    多数据库实现差异

    EF和EFCore是支持多个数据库,其原理是EF定义的前文所说的中间表达式,然后再交给各方自行实现后续的操作。这种设计有如下缺点:

    • 数据库支持绑定了数据访问组件,例如MySQL.Data.Entity这个组件必须需要在MySQL.Data组件下工作,这时如果你想换个访问MySQL的组件是不可以的。

    • 数据库提供程序实现代价太大,如果大家有时间可以去看下EntityFramework.SqlServer或MySQL.Data.Entity里的实现代码,这些都是著名公司微软和Oracle维护的代码,其中的实现代码非常复杂,对于一般的团队而言可以算是一个非常大的项目。

    • 数据库提供程序实现质量,还是以MySQL.Data.Entity为例子,每个提供程序并不像微软那样完全实现EF在SQL Server中的各个功能,而且代码非常复杂问题会很多,即使是Oracle所维护的代码也是同样糟糕,下面分享MySQL.Data.Entity的两个例子。

    MySQL.Data.Entity问题一:这个组件在MySQL5.6由于数据库的主键限制在700多个字节,因此它在自动迁移数据库操作时会报错主键过长错误,等于这个是完全不能用的。

    MySQL.Data.Entity问题二:这个组件在MySQL5.7中,由于数据库的BUG(虚拟列如"SELECT 1"的IS NULL判断错误问题)会导致特定的LINQ表达式生成的SQL脚本是不能得到正确结果的。

    MySQL.Data.Entity问题三:对于继承的SQL生成这一部分是完全没有实现的,你会发现所生成的脚本都是错的。

    MySQL.Data.Entity问题四:EF的Include操作的具体实现是依赖于CROSS APPLY(SQL Server)语法的,不过MySQL中完全没有,也很写出替代语句,因此这个功能在MySQL下等于是不存在的。

    以上是因为我参加了EF对接MySQL的改造项目,这是我和我的团队折腾了几个月才得出的一些总结,不过好在我们最后通过修改MySQL.Data.Entity源码解决了这些问题。

    总之其他框架不能确定,但是在EF中各个数据库的支持程度是很不对称的。

    总结

    在EntityFramework长达10年的发展历程中,开始发展很快,但是后面到EntityFramework6.1.3(2015年3月)这个版本时就好像是EntityFramework的结束,之后EntityFramework6就再没有此实制上的更新了直到今天,应该是微软已经放弃它转向EntityFrameworkCore上,不过EntityFrameworkCore的发展也没有这么快,至今还没有超过EntityFramework6,所以直到今天微软都不敢对外宣布放弃EntityFramework6。

     

    以上是我的个人见解,仅供参考。

    原文地址 http://www.cnblogs.com/CarefreeXT/p/8729085.html


    .NET社区新闻,深度好文,欢迎访问公众号文章汇总 http://www.csharpkit.com

    本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/321719.shtml

    如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

    相关文章

    20、java中的类加载机制

    1、类加载机制是什么? 类加载机制指的就是jvm将类的信息动态添加到内存并使用的一种机制。 2、那么类加载的具体流程是什么呢? 一般说类加载只有三步:加载、连接和初始化,其中连接包括验证、准备和解析,用于将运行时加…

    nssl1447-小智的糖果【dp】

    正题 题目大意 长度为nnn的序列&#xff0c;mmm个位置要求两边都比他大&#xff0c;kkk个位置要求两边都比他小。求序列个数。 解题思路 若第xxx个位置为山峰&#xff0c;那么ax−1<ax>ax1a_{x-1}<a_x>a_{x1}ax−1​<ax​>ax1​&#xff0c;我们用upiup_iu…

    21、java中的反射机制

    先推荐安装一个 eclipse 的反编译插件 Enhanced Class Decompiler 是什么&#xff1f; 在说反射之前先说一下编译时类型和运行时类型&#xff0c;大家都知道List是一个接口&#xff0c;它是不可以被实例化的&#xff0c;但是可以通过多态实现&#xff1a;List list new Arra…

    【北京】BXUG第12期活动基于 .NET Core构建微服务和Xamarin

    分享主题&#xff1a;基于 .NET Core构建微服务实战分享分享者&#xff1a;薛锋 北京切尔思科技架构师 兼任东北大学信息安全工程师和技术主播&#xff0c;行业内专注于研究 .NET Core和Web应用&#xff0c;具有比较扎实的技术基础和数年的从业经历。在GitHub上主持数个开…

    2017西安交大ACM小学期数据结构 [树状数组,极大值]

    Problem D 发布时间: 2017年6月28日 10:51 最后更新: 2017年6月28日 16:38 时间限制: 1000ms 内存限制: 32M 描述 给定一个长度为n的序列a1, a2, ..., an当k满足2≤k≤n−1, ak>ak−1且ak>ak1时, 将元素k称为极大值点, 给出q个操作, 操作分为两种 对于形如1xy的操作…

    nssl1448-小智过马路【模拟】

    正题 题目大意 nnn个横向道&#xff0c;若干辆车&#xff0c;每辆车速度恒定&#xff0c;给出方向位置长度。 过马路的速度&#xff0c;最早开始时间&#xff0c;最晚开始时间。求最长的可以通过马路的时间段。 解题思路 计算出每辆车限制的时间区间&#xff0c;然后排序找到…

    22、java中的注解

    注解是什么&#xff1f; 注解可以理解成注释、标记、标签的意思&#xff0c;用来标记类、方法等。就相当于现实生活中的一些事物&#xff0c;上边贴一个标签或者写一些注释性文字来描述它可以用来做什么、怎么用、何时用等信息。Java中的注解也是一样的&#xff0c;用来表示被标…

    谈谈ASP.NET Core中的ResponseCaching

    前言前面的博客谈的大多数都是针对数据的缓存&#xff0c;今天我们来换换口味。来谈谈在ASP.NET Core中的ResponseCaching&#xff0c;与ResponseCaching关联密切的也就是常说的HTTP缓存。在阅读本文内容之前&#xff0c;默认各位有HTTP缓存相关的基础&#xff0c;主要是Cache-…

    2017西安交大ACM小学期数据结构 [树状数组 离散化]

    Problem E 发布时间: 2017年6月28日 12:53 最后更新: 2017年6月29日 21:35 时间限制: 1000ms 内存限制: 64M 描述 给定一个长度为n的序列a1, a2, ..., an给定两个整数L, R输出有多少个二元组(x,y),x≤y, 满足L≤∑yixai≤R9104≤n≤105, −109≤ai≤109, −109≤L≤R≤10…

    使用 dynamic 类型让 ASP.NET Core 实现 HATEOAS 结构的 RESTful API

    上一篇写的是使用静态基类方法的实现步骤: 使用dynamic (ExpandoObject)的好处就是可以动态组建返回类型, 之前使用的是ViewModel, 如果想返回结果的话, 肯定需要把ViewModel所有的属性都返回, 如果属性比较多, 就有可能造成性能和灵活性等问题. 而使用ExpandoObject(dynamic)就…

    23、java中的网编基础

    什么是网络编程&#xff1f; 在说网络编程之前要先知道什么是网络&#xff0c;网络是一种实现资源共享和数据传输的系统。而网络编程就是使用代码编写程序来进行网络之间数据的传输。使用java进行网络之间数据的传输是比较简单的&#xff0c;java中提供了一些现成的类供我们使…

    2017西安交大ACM小学期数据结构 [又是树状数组、异或]

    Problem F 发布时间: 2017年6月28日 10:31 最后更新: 2017年6月29日 21:35 时间限制: 2000ms 内存限制: 64M 描述 给定一个nm的矩形, 初始时所有元素都为0给出q个操作, 操作有三种 对于形如1x的操作, 将第x行的所有元素异或1对于形如2y的操作, 将第y列的所有元素异或1对于…

    P2717-寒假作业【逆序对,树状数组】

    正题 题目链接:https://www.luogu.com.cn/problem/P2717 题目大意 nnn个数&#xff0c;求有多少个连续子序列的平均值大于等于kkk。 解题思路 因为长度会十分干扰&#xff0c;所以我们将所有数减去kkk。问题就变为了求有多少连续子序列的和非负。用前缀和逆序对求就好了。 co…

    使用 BenchmarkDotnet 测试代码性能

    先来点题外话&#xff0c;清明节前把工作辞了&#xff08;去 tm 的垃圾团队&#xff0c;各种拉帮结派、勾心斗角&#xff09;。这次找工作就得慢慢找了&#xff0c;不能急了&#xff0c;希望能找到个好团队&#xff0c;好岗位吧。顺便这段时间也算是比较闲&#xff0c;也能学习…

    24、jdbc操作数据库(1)

    什么是jdbc&#xff1f; 看一下官方怎么说&#xff0c;JDBC 英文名Java DataBase Connectivity&#xff0c;使用java连接数据库的工具&#xff0c;就是一组使用java代码来执行SQL语句的API。 Jdbc有什么用&#xff1f; 数据库有多种&#xff0c;并且不同数据库操作时的方式和…

    jzoj3918-蛋糕【二分】

    正题 题目链接:https://jzoj.net/senior/#contest/show/2953/0 题目大意 n∗mn*mn∗m的矩阵&#xff0c;有数字&#xff0c;横着三刀竖着三刀分成16份使得最小那份最大。 解题思路 暴力枚举竖着的三刀&#xff0c;然后二分答案判定即可。 codecodecode #include<cstdio&g…

    2017西安交大ACM小学期数论 [阅兵式]

    阅兵式 发布时间: 2017年6月25日 12:53 最后更新: 2017年7月3日 09:27 时间限制: 1000ms 内存限制: 128M 描述 阅兵式上&#xff0c;将士们排成一个整齐的方阵&#xff0c;每个将士面朝前方。问正中心的将士能向前看到几个将士&#xff1f;注意&#xff0c;一条直线上的将…

    25、jdbc操作数据库(2)

    说一下使用jdbc时涉及到的一些基本的接口和类 java.sql.Driver 是数据库驱动接口&#xff0c;com.mysql.jdbc.Driver是mysql对应的驱动&#xff0c;由数据库供应商实现&#xff0c;用于提供驱动&#xff0c;实现了java.sql.Driver接口。 java.sql.DriverManager 管理驱动的…

    jzoj3919-志愿者【换根法,线段树,树形dp】

    正题 题目链接:https://jzoj.net/senior/#main/show/3919 题目大意 nnn个点kkk个需要到达的点&#xff0c;然后求每个点出发经过这些点的最短路径。 解题思路 因为不用回去&#xff0c;答案就是以这点为根链接所有点的树减去离这个点最远点的距离。 我们用线段树维护最远点距…

    2017西安交大ACM小学期数论 [水题]

    水题 发布时间: 2017年6月25日 14:06 最后更新: 2017年7月3日 09:27 时间限制: 1000ms 内存限制: 128M 描述 平均因数个数的统计对于估算数论题目复杂度具有非常重要的意义。小A同学听了今天的课后&#xff0c;于是想要自己写一个程序&#xff0c;求出1到n的平均因数个数…