Составление множества цитат в запросы linq
Я работаю над проектом, в котором я пытаюсь использовать 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 ответ
К сожалению, у меня нет хорошего ответа на этот вопрос.
Боюсь, что переводчик 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) @> )
Проблема заключается в том, что если вы включите лямбда-функции в цитату, транслятор F# должен с ними справиться - возможно, путем их частичной оценки (потому что в противном случае транслятор LINQ to SQL потерпит неудачу). В этом есть немало хитрых дел...
Тем не менее, команда F# в последнее время делает некоторые улучшения в этой области. Я думаю, что лучшее, что можно сделать, - это найти простой случай воспроизведения и отправить его в fsbugs на microsoft dot com. Релизы PowerPack не настолько "чувствительны", поэтому вы можете получить исходный код с последними изменениями, если попросите и предложите помощь с тестированием (но без обещаний).