2012-02-03 4 views
8

dans LINQ, .Lorsque prend une expression> prédicat, que je peux écrire en F # commeExpression <Func <T, bool>> d'un F # func

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

J'utilise FSharp.Powerpack pour construire l'expression d'un citation, mais ce que cela me donne est une MethodCallExpression. En regardant en profondeur, le code powerpack construit le lambda correctement, mais l'enveloppe dans un appel Convert (pourquoi est-ce?). Je me demande si le fait de lancer l'argument à l'appel de méthode (un lambda) me donnerait finalement l'expression> dont j'ai besoin.

Donc la question est de savoir pourquoi l'appel Convert, et comment obtenir réellement le lambda avec la signature Func.

+0

hey, a fait vous trouvez une manière plus simple de faire ceci? – nicolas

+0

sur un sidenote, il ya une question connexe ici, (pas avec des citations cependant) http://stackoverflow.com/questions/3392000/interop-between-f-and-c-sharp-lambdas – nicolas

Répondre

12

Je ne me souviens pas du haut de ma tête où j'ai trouvé ce morceau de code, mais c'est ce que j'utilise pour convertir un Expr<'a -> 'b> en Expression<Func<'a, 'b>>. J'espère que cela va résoudre votre problème.

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() 'est maintenant dans' F # 'en tant que' Microsoft.FSharp.Linq.RuntimeHelpers.LeafExpressionConverter.QuotationToExpression expr' – Maslow

4

Une façon vous pouvez faire est de tirer profit du fait que F # effectuera cette conversion automatiquement lors de l'appel des méthodes sur les types .NET qui attendent une Expression<Func<...>>. Je ne suis pas tout à fait sûr quand cela a été ajouté à la langue, mais certainement avec F # 4, vous n'avez pas besoin de convertir explicitement les expressions F # en celles de LINQ. Si la raison pour laquelle vous vouliez faire en premier lieu devait être en mesure d'utiliser IQueryable API LINQ (ou d'autres API .NET basées sur des expressions) alors maintenant fonctionne sans effort, par exemple:

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

juste travaux. Même si cela ressemble à un lambda ordinaire (nous n'avons pas utilisé la syntaxe d'expression de F #), cela compile en un code qui produit un objet d'expression F #, puis le passe à LeafExpressionConverter‌​.QuotationToExpressi‌on pour le transformer en objet d'expression LINQ.

Mais parfois, vous voudrez obtenir l'objet d'expression de style LINQ directement dans F #. (Par exemple, il est parfois utile d'écrire une fonction F # qui produit une expression que vous utiliserez dans plusieurs requêtes.) Dans ce cas, vous pouvez écrire un aide comme ceci:

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

Cela ressemble il ne fait rien - il retourne juste son argument. Cependant, étant donné que FunAs est un type .NET, F # compilera automatiquement tout site d'appel qui l'appelle avec une expression fun en code qui génère une expression de requête LINQ appropriée. .: par exemple

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

Ici, linqExpr sera de type Expression<Func<MyEntity, bool>>.

La clé de cette méthode est que cette méthode est membre d'un type .NET. Si vous essayez exactement la même chose avec une fonction F # ordinaire:

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

qui semble que cela devrait vouloir dire exactement la même chose que FunAs.LinqExpression, vous constaterez que vous ne pouvez pas appeler de la même manière. Par exemple, si vous essayez ceci:

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

Vous obtiendrez une (un peu inutile) Erreur: « Cette fonction prend trop d'arguments, ou est utilisé dans un contexte où une fonction n'expected`.En faisant de cette fonction un membre d'un type .NET, nous pouvons tirer parti de l'aide de F # "Vous semblez invoquer une API .NET qui attend une expression de style LINQ, laissez-moi m'en occuper" fonctionnalité.

(Il est possible qu'il y ait un peu plus explicite façon de demander au compilateur LINQ pour effectuer cette même astuce pour vous sans apporter un type .NET dans l'image, mais je l'ai pas trouvé.)

Questions connexes