21

ここでクエリ式を見てきました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# はこれとはまったく異なり、述語を作成して結合する方法がいくつかあります。

4

2 に答える 2

32

これは、明示的な引用符を必要とする 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")
于 2012-12-11T20:35:06.697 に答える
6

単純に、元の例では、述語を引用してから、それをつなぎ合わせることができます。

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 が、引用された用語のラムダ抽象化の結果の適用を正規化するという保証はありません。これにより、奇妙なエラー メッセージが表示されたり、クエリのパフォーマンスが低下したりする可能性があります (たとえば、1 つのテーブルをクエリしてから、1 つのクエリ結合を実行する代わりに、最初のテーブルの行ごとに別のテーブルで 1 つのクエリを生成します)。

FSharpComposableQuery私たちは最近、(開いている場合)query演算子をオーバーロードして正規化を実行する (およびその他の役立つことを行う)という名前のライブラリを開発しました。F# クエリ式の重要なサブセットに対して単一のクエリを生成する強力な保証を提供します。FSharpComposableQueryのバージョンのを使用queryすると、上記の素朴な構成が機能します。FSharpComposableQueryまた、既存のクエリ コードを壊さないことを確認するために広範囲にテストしました。

同様に、たとえば を使用するFSharpComposableQueryと、Tomas の例では特別な関数は必要ありません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 型プロバイダーではなく、Northwind の OData バージョンでのみテストしましたが、同様のより複雑な例を多数テストしました。OData バージョンは、OData からの不思議なエラーで失敗しますが、これは当面の問題とは直交しているように見えます。)

FSharpComposableQueryNuGet から入手できるようになりました: https://www.nuget.org/packages/FSharpComposableQuery

詳細情報 (より複雑な形式の構成を示す例と小さなチュートリアルを含む) は、次の場所にあります。

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

[編集: プロジェクト名が変更されたため、上記のリンクを変更して「実験的」という単語を削除しました。]

于 2014-07-30T11:12:01.613 に答える