2012-02-03 29 views
8

en LINQ, .Where toma una expresión> predicado, que puedo escribir en C# comoExpresión <Func <T, bool>> desde un F # func

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

estoy usando FSharp.Powerpack para construir la expresión de una cita, pero lo que me da es un MethodCallExpression. Mirando en profundidad, el código del paquete de poder construye el lambda correctamente, pero lo envuelve en una llamada de conversión (¿por qué es eso?). Me pregunto si lanzar el argumento a la llamada al método (una lambda) finalmente me daría la Expresión> Necesito.

Así que la pregunta es por qué la llamada Convertir, y cómo obtener realmente la lambda con la firma Func.

+0

bueno, lo hizo usted encuentra una manera más simple de hacer esto? – nicolas

+0

en una nota al margen, hay una pregunta relacionada aquí, (aunque no con citas) http://stackoverflow.com/questions/3392000/interop-between-f-and-c-sharp-lambdas – nicolas

Respuesta

12

No puedo recordar en lo alto de mi cabeza dónde encontré este fragmento de código, pero esto es lo que uso para convertir un Expr<'a -> 'b> en Expression<Func<'a, 'b>>. Espero que esto solucione tu problema.

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) 
+4

'expr.ToLinqExpression() 'ahora está en' F # 'como' Microsoft.FSharp.Linq.RuntimeHelpers.LeafExpressionConverter.QuotationToExpression expr' – Maslow

4

Una forma que ahora puede hacer esto es tomar ventaja del hecho de que F # llevará a cabo esta conversión automáticamente cuando se invoca métodos en los tipos de .NET que esperan un Expression<Func<...>>.

No estoy del todo seguro de cuándo se agregó esto al lenguaje, pero sin duda con F # 4, no es necesario convertir explícitamente expresiones F # en LINQ. Si la razón por la que quería hacer esto en primer lugar iba a ser capaz de utilizar las API IQueryable LINQ (u otras API .NET basada en la expresión), entonces ahora sólo funciona sin ningún esfuerzo, por ejemplo:

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

solo trabajos. Aunque parece una lambda ordinaria (no hemos utilizado la sintaxis de expresión de F #), se compila en código que produce un objeto de expresión F # y luego lo pasa a LeafExpressionConverter‌​.QuotationToExpressi‌on para convertirlo en un objeto de expresión LINQ.

Pero a veces querrá apoderarse del objeto de expresión LINQ-style directamente en F #. (Por ejemplo, a veces es útil para escribir una función # F que produce una expresión que se va a utilizar en varias consultas.) En ese caso, se puede escribir un ayudante de la siguiente manera:

type FunAs() = 
    static member LinqExpression<'T, 'TResult>(e: Expression<Func<'T, 'TResult>>) = e 

Esto parece que no hace nada - solo devuelve su argumento. Sin embargo, dado que FunAs es del tipo .NET, F # compilará automáticamente cualquier sitio de llamada que invoca esto con una expresión fun en un código que genera una expresión de consulta LINQ adecuada. Ej .:

let linqExpr = FunAs.LinqExpression(fun (e:MyEntity) -> e.Id = 42) 

Aquí, linqExpr será de tipo Expression<Func<MyEntity, bool>>.

La clave para esto es que este método es miembro de un tipo .NET. Si intenta exactamente lo mismo con una F # función ordinaria:

let funAsLinqExpression<'T, 'TResult>(e: Expression<Func<'T, 'TResult>>) = e 

la que parece que debería significar exactamente lo mismo que FunAs.LinqExpression, encontrará que no se puede llamar de la misma manera. Por ejemplo, si se intenta esto:

let linqExpr = funAsLinqExpression(fun (e:MyEntity) -> e.Id = 42) 

Usted obtendrá un error (un poco inútil): 'Esta función toma demasiados argumentos, o se utiliza en un contexto en el que una función no está expected`.

Al hacer que esta función sea miembro de un tipo .NET, podemos aprovechar la ayuda de F # "Parece que está invocando una API de .NET que espera una expresión de estilo LINQ, déjeme ocuparme de eso por usted" característica.

(Es posible que haya alguna manera más explícita de pedir el compilador LINQ para llevar a cabo este mismo truco para usted sin traer un tipo .NET en la imagen, pero no hemos encontrado.)

Cuestiones relacionadas