2011-02-10 13 views
3

我正在嘗試使用F#和Linq來處理UDF和在SQL服務器中存儲過程的項目。 其中的一部分是靜態定義所有有效的查詢,排序標準以及對查詢結果進行評分的方法。將許多引用合併到linq查詢中

我到目前爲止相當成功,但我遇到了很大的困難,構成了sortBy表達式。

這裏的基本概念

let sorter = 
    let exprMap:Map<string,Quotations.Expr<seq<Product> -> seq<Product>>> = 
    Map.ofList 
    ["ProductName",<@ Seq.sortBy (fun prod -> prod.Name) @> ] 
    // .. more entries .. 
    let sortBuilder sortkeys = 
     Array.foldBack 
     (fun criteria acc -> <@ %(exprMap.[criteria]) >> (%acc) @>) 
     sortkeys 
     <@ Seq.map id @> 

這結束了在查詢執行後使用像這樣

let execQuery = fun (predicates,sorts,scorer) -> 
    <@ seq { for prod in (%dc).Products do 
       if (%predicates) prod then yield prod } 
     |> (%sorts) 
     |> (%scorer) @> 

使用這些基本的輪廓,一切只要我不使用(作品%排序)。每次我將其傳入時,我都無法在F#中將其識別爲Linq翻譯器。我嘗試過使用combinators進行一些不同的嘗試,但我有這種感覺,我錯過了一些東西。如果我將分揀機功能用以下方式取出:

<@ Seq.sortBy (fun prod -> prod.Name) |> Seq.sortBy (fun prod -> prod.Style) @> 

它按預期工作。但是使用的組合子是這樣的:

let (|>*) = fun f g -> <@ fun c -> ((%f) c) |> (%g) @> 

不..

任何想法?

回答

1

不幸的是,我對這個問題沒有很好的答案。

恐怕F#LINQ翻譯器目前對查詢結構非常敏感。使用合成,如果你手工編寫,你應該能夠得到相同的報價,所以你可能需要生成完全一樣的東西,如果手寫的話就可以工作。

例如與分選機,你可能需要像(我沒有嘗試,但我認爲這應該產生相同的報價爲平時的代碼工作):

let (|>*) f g = fun c -> <@ (%c) |> (%f) |> (%g) @> 

<@ seq { for prod in (%dc).Products do 
      if (%predicates) prod then yield prod } @> |> 
(<@ Seq.sortBy (fun prod -> prod.Name) @> |>* 
    <@ Seq.sortBy (fun prod -> prod.Style) @>) 

問題如果在引用中包含lambda函數,F#翻譯器需要處理它們 - 可能是通過部分評估它們(否則LINQ to SQL翻譯器會失敗)。有很多棘手的案例在這...

但是,F#團隊最近在這方面做了一些改進。我認爲最好的辦法是找到一個簡單的repro案件並將其發送到fsbugs at microsoft dot com。 PowerPack版本並不是那種「敏感」的,所以如果你詢問並提供測試幫助(但沒​​有承諾),你可能能夠獲得最近更改的源代碼。