将许多引用组合成linq查询

时间:2011-02-10 20:01:37

标签: linq f# quotations

我正在开发一个项目,我正在尝试将F#和Linq用于UDF,并将存储过程存储在SQL服务器中。 其中一部分是静态定义所有有效查询,排序标准以及对查询结果进行评分的方法。

到目前为止,我已经相当成功了,但是我在编写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) @> 

使用这些基本轮廓,只要我不使用(%sorts),一切都有效。每当我通过时,我都不会被F#认可为Linq翻译。我尝试过使用组合器的不同尝试,但我觉得我错过了一些东西。如果我使用以下

来排除分拣机功能
<@ Seq.sortBy (fun prod -> prod.Name) |> Seq.sortBy (fun prod -> prod.Style) @>

它按预期工作。但是使用这样的组合器:

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

没有..

有什么想法吗?

1 个答案:

答案 0 :(得分: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案例并将其发送到 microsoft dot com fsbugs 。 PowerPack版本不是那么“敏感”,因此如果您询问并提供测试帮助(但没​​有承诺),您可能能够获得最近更改的源代码。