LINQ优于功能方法的优点

时间:2016-07-06 09:17:11

标签: c# sql linq expression-trees kotlin

Kotlin Slack讨论了添加代码树以支持C#LINQ之类的可能性。

在C#中,LINQ有很多应用程序,但我只想关注一个(因为其他人已经可能已经被Kotlin语法覆盖了):将SQL查询组合到远程数据库。

先决条件:

  • 我们在代码中以某种方式表达了SQL数据库的数据模式,以便静态工具(或类型系统)可以检查SQL查询的正确性(至少是命名)

  • 我们必须以字符串

  • 生成查询
  • 我们希望语法接近SQL或Java流

问题:表达式树添加到对手头任务至关重要的语法中会是什么?如果没有它们,SQL构建器有多好?

2 个答案:

答案 0 :(得分:3)

如果没有表达式树,查询dsl有多好?

正如JINQ所示,通过分析字节码可以了解开发人员的意图,从而将谓词转换为SQL。所以原则上表达树对于构建一个外观漂亮的查询不是必不可少的:

val alices = database.customerStream().where { it.name == "Alice" }

即使没有像字节码分析这样的hackery,也可以通过代码生成获得一个不错的查询dsl。 QuerydslJOOQ就是很好的例子。用一点Kotlin包装代码然后你可以写

val alices = db.findAll(QCustomer.customer, { it.name.eq("Alice") }) 

表达式树如何帮助构建查询dsl

表达式树是表示解析为值的某些代码的结构。通过编译器生成这样的结构,不需要字节码分析来理解应该做什么。给出例子

val alices = database.customerStream().where { it.name == "Alice" }

where函数的参数是 expression ,我们可以在运行时检查它并将其转换为SQL或其他查询语言。因为表达式树代表代码,所以不需要在Kotlin和SQL范例之间切换来编写查询。使用linq / jinq表示的查询代码看起来几乎相同,无论它们是使用POCO / POJO在内存中执行还是在使用其查询语言的数据库引擎中执行。编译器还可以进行更多类型检查。此外,使用内存表示替换底层数据库非常容易,可以更快地运行测试。

进一步阅读:

答案 1 :(得分:2)

JOOQ和Querydsl:

ORM的典型解决方案是使用来自应用程序逻辑的DSL或嵌入式DSL。虽然这些方案取得了很大进展,最终以JOOQ和Querydsl为终点,但对于这样一个系统仍有许多警告:

  • 编写这些查询的人所习惯的许多范例(即类型安全)要么在关键方面缺失或不同
  • 确切的语义是不明显的:在上一个答案中,建议我们使用扩展方法eq来执行db-native相等过滤器。新开发人员很可能会错误地使用equals而不是eq
  • 第二点因测试难度而加剧:使用带有虚假数据的实时连接器是一个非常难以解决的问题,因此根据测试程序,可能无法再发现错误的代码jooqDB.where { it.name.equals("alice") }开发管道。

Jinq

Jinq不是第一方数据连接器。虽然我认为这在很大程度上是心身的重要性,但重要的是它。许多项目将使用供应商建议的工具,并且所有主要数据库提供程序都具有Java连接器,因此大多数开发人员可能只是使用它们。

虽然我没有使用Jinq,但我相信Jinq没有看到广泛采用的另一个原因主要是因为它试图使用更强大的域来解决问题:从AST构建查询&# 39; s比从字节代码构建查询容易得多,原因与构建编译器的后端比构建转换编译器相比更容易。虽然我忍不住向Jinq团队倾诉我做了如此出色的工作,但我也不禁认为他们的工具受到了阻碍:用字节码构建查询很难。根据定义,java字节码被提交到在JVM上运行,试图改进对另一个解释器的承诺是一个非常难的问题。

我目前的工作不允许我使用传统的数据库,但如果我要切换项目,知道我需要在DAL中进行大量的数据曝光,我可能会从Kotlin和Java退回到。净,主要是因为Linq,而不是调查Jinq。 "来自Kotlin的Linq"可能会改变我的想法。

数据库供应商的支持:

LINQ-to-SQL和LINQ-to-mongo数据库连接器已在.net社区中得到广泛采用。这是因为它们是第一方,高质量,并且以相当直接的方式行事:将AST编译为SQL(或mongo-query-language)至少在概念上是直截了当的。 ORM的许多传统警告都适用,但供应商(微软和Mongo)继续解决这些问题。

如果Kotlin支持与Linq类似的运行时代码树,并且如果Kotlin继续以当前速率获得牵引力,那么我相信MongoDB和Hibernate团队将很快开始改造其现有的LINQ-to-X支持Kotlin客户端的连接器,最终甚至像微软和IBM这样规模较大的公司也开始支持相同的流程。

来自Kotlin的Linq

更重要的是,Kotlin独特的"接收器类型"在Linq空间中积极实施inline很有意思。 Linq-from-Kotlin可能比LINQ-from-C#更有效。

其中C#有

someInterface
  .where(customer -> customer.Name.StartsWith("A") && ComplexFunctionCallDoneByMongoDriver(customer))
  .select(customer -> new ResultObject(customer.Name, customer.Age, OtherContext()))

Kotlin或许可以取得进步:

someInterface
  .filter { it.name startsWith "A" && inlinedComplexFunctionCallDoneOnDB(it) } 
  //inlined methods would have their AST exposed -> can be run on the DB process instead of on the driver.
  .map { ResultObject(name, age, otherContext()) } 
  //uses a reciever type, no need to specify "customer ->"
  //otherContext() might also be inlined

这是我的头脑,我怀疑比我聪明的大脑可以更好地使用这些工具。

其他用途:

值得一提的是,关于运行时代码AST的应用的假设是错误的:

  

其他[运行时AST问题域] [已]可能已被Kotlin语法

所涵盖

我首先提出这个问题的原因是因为我对Kotlin的无效安全功能及其与Mockito的互动感到恼火:花了一些时间研究这个问题,没有Mocking为Kotlin设计的框架,只有来自Kotlin的可以使用的的java框架,有些痛苦。

java域和Kotlin域中目前尚未解决的一些问题:

  • 模拟框架,如上所述。通过访问AST,Mockito使用的所有关于参数操作顺序的巧妙但奇怪的技巧已经过时了。其他更传统的模拟框架获得了更加直观和类型安全的前端。
  • 绑定表达式,对于UI框架或其他方式,通常会转换为字符串。考虑一个UI框架,开发人员可以在其中编写notifyOfUIElementChange { this.model.displayName }而不是notifyOfUIElementChange("model.displayName")。如果有人重新命名该财产,后者就会陷入僵局的严重问题。
    • 我很高兴看到ControlsFX人或Thomas Mikula可能会对此类功能做些什么。
  • 类似于Kotlin特定的Linq:我怀疑Kotlin的应用程序可能提供了许多我不知道的工具。但我非常有信心他们确实存在。

我真的很喜欢Linq,我不禁想到,由于Kotlin专注于行业问题,Linq-from-Kotlin模块将是一个完美的契合,让许多人生活,包括我的,公平有点容易。