Выражение> из F# func
в linq,.Где принимает выражение> предикат, который я могу написать в F# как
<@ fun item:'a -> condition @> // Expr<'a -> bool>
Я использую FSharp.Powerpack для создания выражения из цитаты, но то, что он дает мне, - это MethodCallExpression. Глядя глубоко, код powerpack правильно строит лямбду, но обертывает ее в вызов преобразования (почему это?). Интересно, если бы приведение аргумента к вызову метода (лямбда), наконец, дало бы мне выражение> мне нужно.
Итак, вопрос почему Преобразование вызова и как на самом деле получить лямбду с подписью Func.
2 ответов
Я не могу вспомнить, где я нашел этот бит кода, но это то, что я использую для преобразования Expr<'a -> 'b>
to Expression<Func<'a, 'b>>
. Надеюсь, это решит вашу проблему.
open System
open System.Linq.Expressions
open Microsoft.FSharp.Quotations
open Microsoft.FSharp.Linq.QuotationEvaluation
let toLinq (expr : Expr<'a -> 'b>) =
let linq = expr.ToLinqExpression()
let call = linq :?> MethodCallExpression
let lambda = call.Arguments.[0] :?> LambdaExpression
Expression.Lambda<Func<'a, 'b>>(lambda.Body, lambda.Parameters)
один из способов сделать это-воспользоваться тем, что F# будет выполнять это преобразование автоматически при вызове методов для типов .NET, которые ожидают Expression<Func<...>>
.
Я не совсем уверен, когда это было добавлено к языку, но, конечно, с F# 4 Вам не нужно явно преобразовывать выражения F# в LINQ. Если причина, по которой вы хотели сделать это в первую очередь, чтобы иметь возможность использовать IQueryable
LINQ APIs (или другие основанные на выражении .NET APIs), то это теперь просто работает без усилий, например:
someEfDataContext.MyEntities.Single(fun e -> e.Id = 42)
просто работает. Хотя это выглядит как обычный лямбда (мы не использовали синтаксис выражения F#), это компилируется в код, который создает объект выражения F#, а затем передает его в LeafExpressionConverter.QuotationToExpression
чтобы превратить его в объект выражения LINQ.
но иногда вы захотите получить объект выражения LINQ-style непосредственно в F#. (Например, иногда полезно написать функцию F#, которая создает выражение, которое вы будете использовать в нескольких запросах.) В этом случае вы можете написать такой помощник:
type FunAs() =
static member LinqExpression<'T, 'TResult>(e: Expression<Func<'T, 'TResult>>) = e
похоже, ничего не делает - он просто возвращает свой аргумент. Однако, потому что FunAs
является типом .NET, F# автоматически скомпилирует любой сайт вызова, который вызывает это с fun
выражение в код, который генерирует подходящее выражение запроса LINQ. Например:
let linqExpr = FunAs.LinqExpression(fun (e:MyEntity) -> e.Id = 42)
здесь linqExpr
будет типа Expression<Func<MyEntity, bool>>
.
ключ к этому заключается в том, что этот метод член типа .NET. Если вы попробуете то же самое с обычной функцией F#:
let funAsLinqExpression<'T, 'TResult>(e: Expression<Func<'T, 'TResult>>) = e
что, похоже, должно означать то же самое, что и FunAs.LinqExpression
, вы обнаружите, что вы не можете назвать его таким же образом. Например, если вы попробуете это:
let linqExpr = funAsLinqExpression(fun (e:MyEntity) -> e.Id = 42)
вы получите (немного бесполезную) ошибку: "эта функция принимает слишком много аргументов или используется в контексте, где функция не ожидается".
, сделав эту функцию членом типа .NET, мы можем воспользоваться полезной функцией F#"вы, похоже, вызываете .NET API, который ожидает выражение в стиле LINQ, позвольте мне позаботиться об этом для вас".
(возможно, есть более явный способ попросить компилятор LINQ выполнить этот же трюк для вас, не вводя тип .NET в картину, но я его не нашел.)