我正在开发一个项目,我正在尝试将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) @>
没有..
有什么想法吗?
答案 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版本不是那么“敏感”,因此如果您询问并提供测试帮助(但没有承诺),您可能能够获得最近更改的源代码。