Вопрос по linq – Как конвертировать Expr <'a ->' b> в выражение <Func <'a, obj >>

5

Я использую F # 3.0 с бета-версией .NET 4.5, и я пытаюсь преобразовать цитату типа F # типаExpr<'a -> 'b> на LINQExpression<Func<'a, 'b>>.

Я нашел несколько вопросов, которые имеют решение этой проблемы, но эти методы, похоже, больше не работают, возможно, из-за изменений в F # 3.0 или .NET 4.5.

Converting F# Quotations into LINQ Expressions Expression<Func<T, bool>> from a F# func

В обоих случаях, когда я запускаю код из решений любого вопроса, следующее действие выдает исключение:

mc.Arguments.[0] :?> LambdaExpression

...гдеmc этоMethodCallExpression, Исключением является:

System.InvalidCastException: Unable to cast object of type 'System.Linq.Expressions.MethodCallExpressionN' to type 'System.Linq.Expressions.LambdaExpression'.

Нет, дополнительный "N" в концеMethodCallExpressionN это не опечатка. У кого-нибудь есть предложение? Благодарю.

UPDATE

Вот полное воспроизведение. Оказывается, этот код прекрасно работает с такими выражениями, как<@ fun x -> x + 1 @>, Моя проблема в том, что в моем случае мне нужно преобразоватьExpr<'a -> 'b> вExpr<'a -> obj> чтобы мне не приходилось засорять все лямбда-выраженияbox, Я сделал это, объединив оригинальное выражение с этим:<@ %exp >> box @>, Это создает объект с правильным типом, но код для преобразования вExpression<Func<'a, obj>> больше не работает.

module Expr =
    open System
    open System.Linq.Expressions
    open Microsoft.FSharp.Quotations
    open Microsoft.FSharp.Linq.QuotationEvaluation

    let rec private translateExpr (linq:Expression) = 
        match linq with
        | :? MethodCallExpression as mc ->
            let le = mc.Arguments.[0] :?> LambdaExpression
            let args, body = translateExpr le.Body
            le.Parameters.[0] :: args, body
        | _ -> [], linq

    let ToFuncExpression (expr:Expr<'a -> 'b>) = 
        let args, body = expr.ToLinqExpression() |> translateExpr 
        Expression.Lambda<Func<'a, 'b>>(body, Array.ofList args) 

let exp = <@ fun x -> x + 1 @>

let r = Expr.ToFuncExpression <@ %exp >> box @>
printfn "%A" r
expr.ToLinqExpression() сейчас вF# какMicrosoft.FSharp.Linq.RuntimeHelpers.LeafExpressionConverter.QuotationToExpression expr Maslow
Тем не мение,<@ fun x -> x |> %exp |> box @> делает компиляцию К сожалению, он получает ту же ошибку, когда я пытаюсь преобразовать его. Joel Mueller
@kvb - Это хорошая мысль, но когда я использую эту конструкцию, она подчеркивает%exp и говорит мне & quot; Это значение не является функцией и не может быть применено & quot; и отказывается компилировать. Joel Mueller
Возможно, вас наказывают за использование стиля без очков. Что произойдет, если вы используете<@ fun x -> %exp x |> box @> вместо? Когда вы используете стиль без точек, то выражение, которое вы конвертируете, не является лямбда-выражением, это приложение. kvb

Ваш Ответ

1   ответ
4

а также включить выражение F #, которое вы пытаетесь преобразовать?

Я попытался протестировать поведение в .NET 4.5, используя минимальный пример, и это сработало для меня. Вот что я сделал:

I created new F# 3.0 project and copied Linq.fs and Linq.fsi from the 2.0 version of F# PowerPack. (Or is there a 3.0 version of the ToLinqExpression method available somewhere in F# 3.0?)

I used the code from Daniel's earlier answer and called the function as follows:

let r = toLinq <@ fun x -> x + 1 @>
printfn "%A" r

This did not throw any exception and it printed x => (x + 1), which looks correct to me.

EDIT: Чтобы ответить на обновленный вопрос - оба примера кода, на которые вы ссылались (мой и Дэниел), предполагают, что тело цитаты является явно сконструированной функцией, поэтому они работают только с цитатами определенной структуры:<@ fun x -> ... @>.

Вы можете решить проблему, используя соединение в явно сконструированной функции. Следующие работы для меня:

let exp = <@ fun x -> x + 1 @> 
let r = toLinq <@ fun a -> box ((%exp) a) @> 
printfn "%A" r

Это содержит применение функции F #, поэтому сгенерированныйExpression содержит призыв кToFSharpFunc (который преобразует делегат в функцию F #), а затем вызывает его. Это может быть проблемой, если вы хотитеExpression это могут понять стандартные инструменты .NET (в этом случае вам придется пост-обработать дерево выражений C # и удалить эти конструкции).

@JoelMueller Спасибо - да, проблема в том, что вы не передаете цитату, содержащую явную лямбду. Смотрите мой измененный ответ.
Я обновил свой вопрос. Оказывается, это может быть связано не с F # 3 или .NET 4.5, а с тем фактом, что я использовал объединение цитат. И да, я скопировал файлы PowerPack Linq в свой проект, как и вы. Joel Mueller
translateExpr функция все еще выдавала (другую) ошибку с вашим обновленным ответом, поэтому я переключил ее на код Дэниела, и теперь она работает. Спасибо! Joel Mueller
К сожалению, AutoMapper (который я в конечном итоге пытаюсь интегрировать) не может обрабатывать любые выражения LINQ, более сложные, чем доступ к свойству. И он не приметExpression<Func<'a, 'b>> на местеExpression<Func<'a, obj>>, Так что я не могу ставить коробку или читать ... Мне нужно задать новый вопрос для этого. Joel Mueller

Похожие вопросы