これは、明示的な引用符を必要とする 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 を記述したり、引用を前処理したりすることで、コードをより適切にすることができると思います。
EDIT:もう少し努力すれば、実際に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")