выражение> из функции F # - PullRequest
       16

выражение> из функции F #

9 голосов
/ 03 февраля 2012

в linq. Где взять предикат Expression>, который я могу написать в F # как

<@ fun item:'a -> condition @>    // Expr<'a -> bool>

Я использую FSharp.Powerpack для построения выражения из цитаты, но оно дает мне MethodCallExpression. Если взглянуть глубже, код powerpack правильно строит лямбду, но оборачивает ее в вызов Convert (почему это так?). Интересно, если приведение аргумента к вызову метода (лямбда-выражения), наконец, даст мне выражение> мне нужно.

Итак, вопрос в том, почему вызов Convert и как на самом деле получить лямбду с подписью Func.

Ответы [ 2 ]

12 голосов
/ 04 февраля 2012

Не могу вспомнить, где я нашел этот фрагмент кода, но это то, что я использую для преобразования Expr<'a -> 'b> в 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) 
7 голосов
/ 17 марта 2017

Один из способов сделать это сейчас - воспользоваться тем, что F # выполнит это преобразование автоматически при вызове методов для типов .NET, ожидающих Expression<Func<...>>.

Я не совсем уверенкогда это добавлено к языку, но, конечно, с F # 4, вам не нужно явно преобразовывать выражения F # в выражения LINQ.Если причиной, по которой вы хотели сделать это, была возможность использовать IQueryable LINQ API (или другие основанные на выражениях .NET API), то теперь он просто работает без усилий, например:

someEfDataContext.MyEntities.Single(fun e -> e.Id = 42)

просто работает.Даже если это выглядит как обычная лямбда (мы не использовали синтаксис выражения F #), она компилируется в код, который создает объект выражения F #, а затем передает его в LeafExpressionConverter‌​.QuotationToExpressi‌on, чтобы превратить его в объект выражения LINQ.

Но иногда вам захочется получить объект выражения в стиле LINQ непосредственно в 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 Type.Если вы попробуете то же самое с обычной функцией 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, позвольте мне позаботиться об этом для вас"feature.

(Возможно, существует более явный способ попросить компилятор LINQ выполнить этот же трюк для вас, не добавляя в изображение тип .NET, но я его не нашел.)

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...