Выражение> из 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‌​.QuotationToExpressi‌on чтобы превратить его в объект выражения 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 в картину, но я его не нашел.)