-->

你如何编写查询表达式在F#?(How do you compose query expression

2019-07-04 23:37发布

我一直在寻找的查询表达式这里http://msdn.microsoft.com/en-us/library/vstudio/hh225374.aspx

我一直在想,为什么下面是合法的

let testQuery = query {
        for number in netflix.Titles do
        where (number.Name.Contains("Test"))
    }

但是,你真的不能做这样的事情

let christmasPredicate = fun (x:Catalog.ServiceTypes.Title) -> x.Name.Contains("Christmas")
let testQuery = query {
        for number in netflix.Titles do
        where christmasPredicate 
    }

当然F#允许这样的组合性,因此可以重用谓词? 如果我想要的圣诞职称就像一个特定日期之前另一个谓词结合起来呢? 我要复制并粘贴我整个查询? C#是完全不同这一点,有几个方法来建立和合并谓词

Answer 1:

这是很容易与F#2.0的版本,需要明确的报价单的查询做(我写了一篇博客文章吧 )。 有一种方法,以实现在C#中类似的事情(另一个博客文章 ),我认为类似的招数可以用F#3.0中播放。

如果你不介意丑陋的语法,那么你可以使用F#3.0太露骨报价。 当你写
query { .. }编译器实际上产生类似:

query.Run(<@ ... @>)

其中内的代码<@ .. @>是引用F#代码-即,存储在代码Expr表示源代码,并且可以被转换为LINQ表达式,从而SQL类型。

这里是我与测试了一种示例SqlDataConnection类型提供者:

let db = Nwind.GetDataContext()

let predicate = <@ fun (p:Nwind.ServiceTypes.Products) -> 
  p.UnitPrice.Value > 50.0M @>

let test () =
  <@ query.Select
      ( query.Where(query.Source(db.Products), %predicate), 
        fun p -> p.ProductName) @>
  |> query.Run
  |> Seq.iter (printfn "%s")

关键的诀窍是,当你使用明确的报价单(使用<@ .. @>您可以使用%的报价切片运算符。 这意味着,报价predicate放入查询(在报价test在你写的地方) %predicate

相比于漂亮的查询表达式的代码是相当难看,但我怀疑,你可以通过在此之上编写一些DSL或预处理的报价使它更好。

编辑:随着多一点的努力,它实际上是可以使用query { .. }再次语法。 你可以引述整个查询表达式和写入<@ query { .. } @> -这不会直接合作,但你可以采取的报价,并提取查询的实际身体并把它传递给query.Run直接。 下面是对上面的例子中的工作原理的示例:

open System.Linq
open Microsoft.FSharp.Quotations
open Microsoft.FSharp.Quotations.Patterns

let runQuery (q:Expr<IQueryable<'T>>) = 
  match q with
  | Application(Lambda(builder, Call(Some builder2, miRun, [Quote body])), queryObj) ->
      query.Run(Expr.Cast<Microsoft.FSharp.Linq.QuerySource<'T, IQueryable>>(body))
  | _ -> failwith "Wrong argument"

let test () =
  <@ query { for p in db.Products do
             where ((%predicate) p)
             select p.ProductName } @>
  |> runQuery
  |> Seq.iter (printfn "%s")


Answer 2:

天真的,在最初的例子一个可以尝试引用断言,然后在拼接它:

let christmasPredicate = <@ fun (x:Catalog.ServiceTypes.Title) -> 
                             x.Name.Contains("Christmas") @>
let testQuery = query {
        for number in netflix.Titles do
        where ((%christmasPredicate) number) 
        select number
    }

(我已经采取稍微清理原来的例子的自由)

象这样的例子(简单的,一阶的λ-抽象)经常在F#工作无论如何,但在一般情况下,有没有保证,F#的默认的QueryBuilder将正常化走在引用术语产生的λ-抽象的应用。 这可能会导致奇怪的错误信息,或与业绩不佳查询(例如查询一个表,然后生成每第一个表的行另一个表一个查询,而不是做一个单个查询加入)。

我们最近开发了一种名为库FSharpComposableQuery是(如果打开)重载query操作执行规范化(以及做其他一些有用的东西)。 它提供了强有力的保障,以产生F#查询表达式的非平凡子集的单个查询。 使用FSharpComposableQuery的的版本query ,上述天真组成的作品。 我们还测试了广泛的尝试,以确保FSharpComposableQuery不破坏现有的查询代码。

同样,例如,使用FSharpComposableQuery ,托马斯的例子并不需要特殊RunQuery功能。 相反,人们可以简单地这样做:

open FSharpComposableQuery

let predicate = <@ fun (p:Nwind.ServiceTypes.Product) -> 
                     p.UnitPrice.Value > 50.0M @>
let test () =
  query { for p in db.Products do
          where ((%predicate) p)
          select p.ProductName }
  |> Seq.iter (printfn "%s")

(警告:我只只罗斯文,而不是SQL类型提供的OData的版本测试上面的代码,但我们已经测试了大量的类似的和更复杂的例子中的OData的版本无法与OData的一个神秘的错误,但是。这似乎正交手头的事情。)

FSharpComposableQuery现在可以从这里的NuGet: https://www.nuget.org/packages/FSharpComposableQuery

和更多信息(包括实施例和一个小的教程,展示的组成更复杂的形式)可以在这里找到:

http://fsprojects.github.io/FSharp.Linq.ComposableQuery/

[编辑:更改上面的链接删除单词“实验”,因为项目名称已更改。]



文章来源: How do you compose query expressions in F#?