对于需要显式引用的 F# 2.0 版本的查询,这很容易做到(我写了一篇关于它的博客文章)。有一种方法可以在 C#(another blog post)中实现类似的东西,我认为类似的技巧可以用 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
放入查询的引文 (in 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")