这个可以用 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")
where predicate
。你可以使用Where(predicate)
,但我认为在这里也可以做同样的事情。 - svickwhere (christmasPredicate number)
行不行? - Ramon Snir