【发布时间】:2014-03-15 06:19:29
【问题描述】:
我正在设计一个流畅的 API 来编写 SQL。请记住,我的目标之一是让 API 不建议在链的该部分中无法调用的函数。例如,如果您刚刚在 select 子句中定义了一个字段,那么在您首先调用 From 之前,您不能调用 Where。一个简单的查询如下所示:
string sql = SelectBuilder.Create()
.Select()
.Fld("field1")
.From("table1")
.Where()
.Whr("field1 > field2")
.Whr("CURRENT_TIMESTAMP > field3")
.Build()
.SQL;
我的问题来自 SQL 代码中的递归。假设你想让一个字段包含另一个 SQL 语句,如下所示:
string sql = SelectBuilder.Create()
.Select()
.Fld("field1")
.SQLFld()
.Select
.Count("field6")
.From("other table")
.EndSQLFld()
.FLd("field2")
.From("table1")
.Where()
.Whr("field1 > field2")
.Whr("CURRENT_TIMESTAMP > field3")
.Build()
.SQL;
我正在使用方法链接来构建我的流畅 API。它在很多方面都是一个状态机,分布在代表每个状态的许多类中。要添加此功能,我基本上需要复制我已经拥有的每个状态并将它们包装在两个 SQLFld 和 EndSQLFld 状态周围。如果您再下一层并将 SQL 语句嵌入到已经嵌入的 SQL 语句的字段中,我将需要另一个副本。这继续到无穷大,因此对于无限深的嵌入式 SQL 查询,我需要无限数量的类来表示无限状态。
我考虑编写一个 SelectBuilder 查询,该查询被带到 Build 方法的位置,然后将该 SelectBuilder 嵌入到另一个 SelectBuilder 中,从而解决了我的无穷大问题,但它不是很优雅,这就是这个 API 的重点.
我也可以抛弃 API 仅在适当时提供函数的想法,但我真的很讨厌这样做。我觉得这可以帮助您最好地发现如何使用 API。在许多流畅的 API 中,调用什么顺序并不重要,但我希望 API 尽可能接近实际的 SQL 语句并强制执行其语法。
有人知道如何解决这个问题吗?
【问题讨论】:
-
我还以为你在谈论递归 CTE。请注意,子查询可以包含对外部环境的引用(实际上是 正常 情况)。子查询也可以用作表引用(
FROM/JOIN子句)和WHERE子句。我希望您不只是将字符串连接在一起,否则人们可能会认为这是防 SQL 注入的(因此,您至少需要提供一个变量参数化方法)。当然,C# 已经有了 Linq-to-SQL,虽然有点不同。