LightSpeed——针对.NET 的商业化 ORM

  • Roopesh Shenoy
  • 侯伯薇

2012 年 2 月 29 日

话题:C#语言 & 开发架构

LightSpeed是一种针对.NET 的商业化 ORM,它拥有多种特性,像实体序列化、健壮的 VS 设计器、内建的 LINQ 支持、对 DTO 的支持等等。我们联系了 Mindscape(开发 LightSpeed 的公司)的共同创始人 John-Daniel Trask,对产品进行了深入探讨,并在总体上对 ORM 进行了讨论。

InfoQ:现在已经有了很多种开源或者商业化的针对.NET 的 ORM 产品,你为何决定要创建一种新的产品呢?

JD:LightSpeed 是 Mindscape 的第一款产品,我们是基于之前在 IT 服务领域的经验来创建的。我们使用过现存的对象 - 关系映射器(ORM),但是那时在.NET 领域并没有太多选择。要么使用 NHibernate,要么就从头创建自己的工具,而很多组织都构建了自己的 ORM。我们觉得现存的 ORM 在性能上和使用的体验上都不是很好,同时也相信业务部门会乐于为优良的、易于学习、高性能的 ORM 付费,相对于其它产品或者 DIY 的方法来说,那会节省他们很多的时间。

InfoQ:你认为 ORM 应该处理的关键问题有哪些?

JD:ORM 的基本目的就在于为数据库和对象世界提供良好的映射关系,所以当然需要把这一点做好。

除此之外,性能非常重要。最常见的抱怨就是 ORM 的性能不好,尽管我们并不确信是那样。好的 ORM 可以将很多技术自动化,像对象融合(object hydration)、查询批处理、有效地生成查询、热切载入(eager loading)、智能缓存等等,那会让基于 ORM 的代码和手动编写的代码执行得一样快。

生产率也是关键的问题。使用 ORM 不是要节省计算机的时间,而是要节省开发时间——更快地构建应用,消除各种错误、执行好的实践、让快速可靠的测试和部署成为可能。例如,LightSpeed 会自动对查询进行参数化,那和手动编写的 SQL 相比,会避免大多数 SQL 注入的攻击。我们都需要花费时间创建模型来实现强验证,或者数据绑定通知,或者编辑回滚。有了 LightSpeed 我们努力把这些类型的问题归到“拿来就用(it just work)”一类。从原始的数据访问代码转为使用 ORM 就像从汇编转为 C# 或者 Java 一样。可能你会放弃一点儿性能,但是比你想象的要少,而你换回的是上百倍的生产效率。

最后一点特别针对.NET ORM 的是对 LINQ 的支持。我们已经大量使用了 LINQ,很难想象如何在没有它的情况下工作。所以拥有真正稳定的 LINQ 提供程序非常重要。构建坏的 LINQ 提供程序很难。构建好的 LINQ 提供程序更难。多年以来我们一直在改善 LightSpeed 中的 LINQ 提供程序,这是我们值得自豪的一点。对 LINQ 的支持已经成为.NET 开发者工具箱中期望得到的特性,所以一种 ORM 中不支持 LINQ 或者支持很差都是不可接受的。

InfoQ:LightSpeed 支持类似于 EF 使用 POCO 实现的代码先行(code-first)之类的功能吗?

JD:模型先行和代码先行在概念上非常类似,它们都是说开发者在数据库结构存在之前以代码的方式来说明模型。LightSpeed 当然支持模型先行,而且早在多年前就已经支持,然而,那依赖于使用 LightSpeed 的模型设计器创建和管理数据库的变更,并基于定义的模型来创建类。EF 的代码先行比模型先行更进一步,它让开发者可以只编写类本身,然后就可以从那些类生成对数据库的更新,而不需要设计器。我们几乎没有收到添加这种支持的请求,因为它会造成生产力的极大降低,那是由于需要你手动编写所有应用程序所需的实体,然后才能创建类。

关于 POCO,我们不会把它设置为默认的选项。LightSpeed 实体依赖于“Entitiy”基类,坦白说,这是因为我们相信它会为开发者带来巨大的价值。记住,ORM 是与生产力相关的。我们看到喜欢 POCO 的人们花费多个小时来管理 T4 模板,只是为了生成支持的类,比方说 INotifyPropertyChanged,或者跟踪改变了的字段,或者处理验证(或者更坏,手动编写所有这些代码而不使用代码生成器!) 唉,你有更好的方法,不需要手动编写那类代码。我们的经验是,天哪,用额外的继承层次来换取这些内容是很值得的:LightSpeed 的开发者通常会喜欢它,因为他们可以专注于解决实际的问题,而不是手动继承或者编写他们自己的样板代码(plumbing code)。

这么说,分布式场景会从 POCO 获得很大的益处,因此 LightSpeed 支持两种方法来进行分布式开发:

  • 通过线(wire)和“DistributedUnitOfWork”进行实体序列化,从而开发者可以在客户端编写 LINQ 查询,然后 LightSpeed 会自动把它传送给服务器,运行查询,然后再把结果发送回来。我们还会做一些聪明的事情,像只把变更的实体发送回来,从而让速度更快。这种方法功能更完整,并且会被视为“有魔力”的。
  • DTO——LightSpeed 设计器不仅可以为用户生成 DTO,而且我们会提供方法,使得把 DTO 导入到服务器上完全成熟的 Entity 对象中更加容易。这很简单,不那么神奇,但是有些开发者更喜欢这样。

InfoQ:关于自动迁移情况如何呢?

LightSpeed 支持比自动迁移更好的功能。

LightSpeed 是唯一一种扩展了 Visual Studio,添加了集成的 schema 迁移管理的 ORM。这包括捕获变更,显示存在什么样的迁移,针对数据库执行它们,并生成变更脚本(以任何你喜欢使用的数据库)。当然,服务器上不应该安装 Visual Studio,因此我们对于生产数据库会包含命令行工具来运行迁移操作。或者你可以使用一种 API 把它集成到应用程序当中。

迁移只是事情的一部分——一旦你确定模型的改变是你所需要的,那么通常就会做出迁移。LightSpeed 设计器从我们发布的时候就支持名为“360 度的数据库往返(360 database round tripping)”特性,用户非常喜欢这种特性。它的意思是,如果你是喜欢使用模型先行的开发者,那么你就可以先对领域建模,右键点击“更新数据库”,然后 LightSpeed 设计器就会在开发数据库和模型之间执行寻找差异的操作,并让你确认是否想要应用变更。当完成的时候点击“OK”,数据库就更新了。

但是如果你使用数据库先行的方式呢? 很简单,右键点击你的模型,选择“从数据库更新”,LightSpeed 的模型设计器就会找到变更,展示给你,让你确认你是否想要应用到实体上。最神奇的事情是,你可以使用混合的方式,如果某些开发者喜欢模型先行,而另一些喜欢数据库先行,他们可以按照喜欢的方式工作,一切都可以同时进行,那样就会生效。

InfoQ:你曾经在一篇博文中提到,LightSpeed 在单独一个实体模型设计器可以支持最多 2000 个实体,并且还能够保持用户的友好性,请你说明一下这是如何达到的。

JD:在一个模型中放 2000 个实体对用户来说永远都不会是友好的,但是我们已经做了很多努力帮助用户使用非常复杂的模型。

例如,我们提供了一种快速的方式在模型中查找你所需要的实体——只需要键入它的名字,我们就会进行过滤,并找到匹配的实体。然后,你可以对其扩展,以包含相关的实体或者整个集合。如果你想要看到整个子领域——比方说销售或者物流——而不是单独的集合,那么你可以对实体设置标签,并通过标签来过滤。所以我们很容易就可以从模型返回到你刚刚感兴趣的那几个实体。你还可以通过多个文件来扩展模型——例如,每个子领域一个文件——那并非是整合的方式,但是,如果多个人员同时操作模型的不同部分,那有助于解决源代码控制的问题。

实体着色是一种看起来很微不足道的特性,但是对于大型的模型能够起到帮助。通常在你的模型中会有相对较少的关键业务实体,而模型的其他部分都围绕这些关键实体。以可区分的颜色来显示那些实体,这样一下子就能够很容易地找到它们——它们在各种噪音中显得很特别。

在低层次,我们还在快速获取数据库元数据方面付出了很多努力。例如,我们避免拉取不需要的表的元数据——当你处理不规则的遗留数据库时,这会导致很大的不同。

所以这意味着即便在数据库更大的情况下,我们也可以高效地完成 schema 的 round-tripping。

InfoQ:对于当前使用 NHibernate 或者实体框架的应用程序来说,是否有一种简单的迁移方式,而不需要对代码做出太多变更?

JD:这在很大程度上取决于开发者如何建立应用程序的架构。

如果开发者使用了 LINQ(例如,使用实体框架或者 LINQ to SQL),那么就很容易。我们有的客户只花费了几个小时,就把构建在实体框架上的大型应用程序转换为 LightSpeed。之所以简单,是因为如果查询已经在 LINQ 中,那么就不需要重新编写,用户只需要使用 LightSpeed UnitOfWork 来替换会话对象,把模型迁移到 LightSpeed 模型,并对应用程序添加一些配置。相反,如果大量查询逻辑都是用 HQL(Hibernate Query Language)编写的,那么你就有大量工作要做!

对于模型迁移来说,LightSpeed 包含了一些特性来帮助把实体框架和 LINQ to SQL 的模型迁移到 LightSpeed。把 EDMX 文件或者 LINQ to SQL 模型文件拖拽到 LightSpeed 模型的界面上,就会让 LightSpeed 设计器读取模型并自行生成。然而,要记住,模型迁移通常只是过程的开始而不是结束。测试很重要——例如,你可能发现在 LINQ 提供程序,特别是带有非常复杂的查询的提供程序中会有区别。

就像在所有迁移过程中一样,你会想要避免受到目标平台的影响。例如,你可能想要把你的验证代码迁移到集成的验证框架中,设置热切载入(eager load)使你的查询效率更高,诸如此类。但是在一般的 LINQ 语法和模型导入支持之间,至少尝试迁移到 LightSpeed 是很容易的,然后就可以看下它是否适合你的应用程序。

InfoQ:依你所见,哪些通常需要的特性不在 ORM 的责任范围之内,为什么?

JD:我们遇到过一些开发者,他们希望 ORM 能够完全取代与数据库交互的工作。例如,要求 ORM 管理数据库复制,或者是一些通常是数据库关注的问题。尽管这些特定的特性不在 ORM 的范围之内,并且理论上可以添加到其中,但是很少有用户会利用这些特性,因此包含那些特性在成本上是不合算的。大多数那类特性我们是不会支持的。

查看英文原文:LightSpeed – A Commercial ORM For .NET
C#语言 & 开发架构