根据属性参数化F#查询

时间:2017-07-23 05:26:09

标签: linq f# type-providers quotations

我希望在几个表中分解出一些常见的查询。在一个非常简单的示例中,所有表都有DataDate列,因此我有这样的查询:

let dtexp1 = query { for x in table1 do maxBy x.Datadate }
let dtexp2 = query { for x in table2 do maxBy x.Datadate }

基于previous question,我可以执行以下操作:

let mkQuery t q  = 
        query { for rows in t do maxBy ((%q) rows) }

let getMaxDt1 = mkQuery table1 (<@ fun q -> q.Datadate @>)   
let getMaxDt2 = mkQuery table2 (<@ fun q -> q.Datadate @>)

如果有任何其他解决方案不使用报价,我会感兴趣。原因在于,对于更复杂的查询,引用和拼接变得难以阅读。

这显然不会起作用,因为我们不知道x具有属性DataDate。

let getMaxDt t = query { for x in t do maxBy x.Datadate }

除非我能抽象出SqlProvider生成的table1,table2等类型。

1 个答案:

答案 0 :(得分:3)

答案很大程度上取决于您需要构建什么类型的查询以及它们的静态或动态。一般来说:

  • LINQ非常棒,如果它们大部分是静态的,并且如果你可以轻松列出所有需要的查询的模板 - 主要的好处是它静态类型检查查询

    < / LI>
  • 当你的查询结构非常动态时,LINQ就不那么好了,因为那样你最终会编写很多引号,而且类型检查有时也会受到影响。

如果您的查询非常动态(包括动态选择源),但不是太复杂(例如没有花哨的分组没有花哨的连接),那么编写代码以从F#域模型生成SQL查询可能更容易。

对于您的简单示例,查询实际上只是一个表名和聚合:

type Column = string
type Table = string

type QueryAggregate =
  | MaxBy of Column

type Query = 
  { Table : Table
    Aggregate : QueryAggregate }

然后,您可以使用以下方式创建两个查询:

let q1 = { Table = "table1"; Aggregate = MaxBy "Datadate" }
let q2 = { Table = "table2"; Aggregate = MaxBy "Datadate" }

将这些查询转换为SQL非常简单:

let translateAgg = function
  | MaxBy col -> sprintf "MAX(%s)" col

let translateQuery q =
  sprintf "SELECT %s FROM %s" (translateAgg q.Aggregate) q.Table

根据查询的丰富程度,翻译可能变得非常复杂,但如果结构非常简单,那么这可能比使用LINQ构建查询更容易。正如我所说,如果不知道确切的用例,很难说会有什么好处!