3

我正在做一个项目,我正在尝试将 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) @>

才不是..

有任何想法吗?

4

1 回答 1

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# 团队最近一直在这方面做了一些改进。我认为最好的办法是找到一个简单的复制案例并将其发送给microsoft dot comfsbugs。PowerPack 版本并不那么“敏感”,因此如果您询问并提供测试帮助(但没​​有承诺),您可能能够获得包含最近更改的源代码。

于 2011-02-10T20:58:07.940 回答