Entity Framework Core 2.0 的槽点

  • Jonathan Allen
  • 薛命灯

2017 年 8 月 23 日

话题:.NET语言 & 开发

Entity Framwork(EF)一直以来毁誉参半,有些人喜欢它,不过有些人认为它与 NHibernate、LINQ-to-SQL 和其他小型 ORM 框架相比没有什么优势。EF Core 在早期给人们留下糟糕的印象,也一直让那些对 EF 有所期待的人大失所望。

SQL 生成

EF 最糟糕的一点是对 SQL 生成这一功能支持不足,而 EF Core 2.0 仍然不支持最基本的 SQL 组建功能(比如 group)。虽说 ORM 框架不支持一些高级特性(如Window Functions)是很正常的,但无法处理像“GROUP BY”这样的功能对于大部分开发者来说是无法接受的。

版本说明里提到,这一版本“增加了一些 SQL 映射模式,之前版本里很多会触发客户端求值的查询在 2.0 里不会再有类似的行为”。不过,他们并没有提及这些模式的具体细节,所以我们建议开发者要小心对待每一个查询。当然,你也可以禁用客户端求值

微软方面指出,他们计划在 EF Core 2.1 里支持 group 特性。

复杂类型(Complex Type)

EF 里有复杂类型的概念,开发者可以创建映射到同一张表的子对象。它的用处之一就是用来把常用的审计字段和一般的数据字段分开。

EF Core 并不支持复杂类型,不过它提供了“Owned”和“Child”类型。微软的 Diego B Vega 说,“EF Core 2.0 的 Owned 类型支持之前 EF 版本的复杂类型所能支持的所有场景”。不过,它们的语法不太一样,而且模型设计得太过复杂,需要花更多的时间来学习它们。

懒加载

懒加载被很多人认为是一个糟糕的设计,因为它会导致性能问题(比如 1+N 查询问题)和运行时错误(比如上下文被销毁)。尽管如此,它仍然是 EF 里非常受欢迎的一个特性,有些人认为,如果一个 ORM 框架不支持这一特性,那么它就算不上一个真正的 ORM 框架。所以把懒加载推迟到 EF Core 2.1这一决定也难怪会让他们感到不安。

微软的 Rowan Miller 说开发者可以创建自己的懒加载机制。

虽然 2.0 不支持懒加载,不过我们提供了一些特性,可以通过这些特性来实现懒加载。其中最重要的一个就是 Life Cycle Hooks,再结合我们在 1.1 版本里就已引入的 EntityEntry API,就可以实现一个初步的懒加载。

不过,用于具体化对象的 Life Cycle Hooks 目前还只是 EF Core 2.1 的“延展目标”。

Table Per Type

Table Per Type(TPT)继承模式可以让一个逻辑记录跨越多个数据库表。一个抽象的基类代表一张表,而每一个子类都有自己对应的表。这样一来,在数据库设计上就会更有效率,而应用程序也能看到数据的逻辑视图。

不过,TPT 在 EF 里有严重的性能问题。以下的抱怨内容来自User Voice

随着子类数量的增长,生成 SQL 需要花费更长时间,SQL 查询也变得越来越复杂,难以管理。一个简单的基类(5 到 6 个字段)和大约 30 个左右的简单子类(2 到 3 个字段)就需要 2 分钟来生成和执行(ObjectQuery.ToTraceString())。EF 会为基类的一个简单 SELECT 查询生成 8000 行 SQL 代码,生成的 SQL 里有很多子查询、join 和 union。即使是空表(查询不返回数据),它也需要那么多时间来生成和执行 SQL。

这里的大部分问题都已经得到修复,不过 EF 6 已经过时了,微软在这方面也没有计划投入更多的资源。

EF Core 完全不支持 TPT,尽管这一特性已经包含在backlog里,但并没有具体的实现计划。这对于已经在 EF 里使用 TPT 的开发者来说会是个问题,因为他们需要作出重大修改。

Table Per Concrete Class

与 TPT 相关的另一个特性是 TPC,也就是 Table Per Concrete Class。与 TPT 类似,它也是通过继承关系来简化类的设计。不过,在数据库端并没有相应的表来表示抽象基类。每一个具体的子类与自己的一张表相对应,这张表包含了子类本身和所有父类的字段。

EF Core 也不支持 TPC,不过似乎没有太多人对此提出看法,可能是因为开发者可以很容易地使用接口来代替抽象基类。基类的属性可以被剪切和拷贝到子类里,虽然这样做很繁琐,但也并没有什么问题。

存储过程

另一个缺失的特性是对存储过程的支持。尽管存储过程有可能被滥用,但对于关系型数据库来说,它仍然是一个强大的工具。在很多情况下,直接在数据库端处理数据要比把数据传送到应用程序里再处理高效得多。特别是当一个事务需要锁定多个表,并多次传送数据的时候,存储过程的优势就更为明显。

这个特性看起来似乎是很容易实现的,因为生成这样的 SQL 非常简单。InfoQ 通过 DLR 来演示一个小型 ORM仅使用了 200 行代码。不过,目前这一特性也只是在 backlog 中,没有具体的实现计划。

因为有临时解决方案的存在,所以这一特性的优先级不会很高。Anuraj P 演示了如何在 EF Core 里调用存储过程,以及如何解决缺乏命名参数的痛点。

空间类型

EF Core 不支持空间数据,虽然它的路线图赋予这一特性较高的优先级,但并没有具体的发布计划。

查看英文原文: Entity Framework Core 2.0 Released to Heavy Criticism

.NET语言 & 开发