2010-04-26 48 views
3

在做LINQ到SQL在C#中,你可以做這樣的事情:可組合FLinq表達式

var data = context.MyTable.Where(x => x.Parameter > 10); 

var q1 = data.Take(10); 
var q2 = data.Take(3); 

q1.ToArray(); 
q2.ToArray(); 

這將產生2個獨立的SQL查詢,一個與TOP 10,和其他與TOP 3。在玩Flinq時,我看到:

let data = query <@ seq { for i in context.MyTable do if x.Parameter > 10 then yield i } @> 

data |> Seq.take 10 |> Seq.toList 
data |> Seq.take 3 |> Seq.toList 

不是做同樣的事情。在這裏它似乎做了一個完整的查詢,然後在客戶端執行「take」調用。我看到的一個替代方案是:

let q1 = query <@ for i in context.MyTable do if x.Param > 10 then yield i } |> Seq.take 10 @> 
let q2 = query <@ for i in context.MyTable do if x.Param > 10 then yield i } |> Seq.take 3 @> 

這兩個生成的SQL使用適當的TOP N過濾器。我的問題是它看起來不可組合。我基本上必須複製「where」子句,並且可能需要複製其他可能需要在基本查詢上運行的其他子查詢。有沒有辦法讓F#給我更多的組合?

(我本來posted this question to hubfs,在那裏我已經得到了一些答案,處理與C#進行查詢轉換「底」,即當需要數據,其中F#是急切地做這種轉變的事實。)

回答

5

要在F#中執行此操作,您需要使用稍微不同的方法。而不是使用方法調用來組成查詢(並且執行不成功),您需要構造帶引號的F#代碼。如果你只需要通過一些數字參數參數化的代碼,你可以寫在運行查詢功能:

let takeData count = 
    <@ seq { for i in context.MyTable do 
      if x.Parameter > 10 then 
       yield i } 
    |> Seq.take count @> |> query 

這種方法只適用於簡單的情況下,由於參數只能是數字。但是,您還可以通過允許您將其他操作添加到核心查詢的方式撰寫F#語句。

例如,假設您想將Seq.takeSeq.sortBy附加到查詢的核心部分。這可以使用所謂的拼接運算符完成。內部報價(內<@ .. @>代碼)可以用一個特殊的operato %,讓您拼接另一個報價爲您正在構建的一個:

let createQuery op = 
    <@ seq { for i in context.MyTable do 
      if x.Parameter > 10 then 
       yield i } 
    |> %op @> |> query 

這裏,op參數是Expr<seq<MyTableRow> -> 'a>類型。您可以使用一些引用作爲參數調用createQuery函數,並且它將在覈心查詢後追加參數。例如:

createQuery <@ Seq.take 10 @> 
createQuery <@ Seq.sortBy (fun x -> x.Parameter) @> 

這實際上比C#允許您執行的功能強大得多。我前段時間寫了兩篇文章,這一點:

  • 顯示了一些F#的例子 - 不幸的是,它使用過時的語法,所以它不會直接工作,但它應該表現出的想法。在舊版本的F#中,您可以在報價中使用_,該報價自動創建了一個需要報價的函數(將拼接代替_作爲參數)。這樣,可以使用(你也不需要再好笑Unicode字符:-))被改寫:

    (fun x -> <@ .. %x .. @>) 
    
  • Composing LINQ queries at runtime in C#展示瞭如何提供一些不直接提供C#的附加功能(使用一些技巧)