in linq, .Where takes a Expression> predicate, which I can write in F# as
<@ fun item:'a -> condition @> // Expr<'a -> bool>
I'm using FSharp.Powerpack to build the expression from a quotation, but what it gives me is a MethodCallExpression. Looking deep, the powerpack code builds the lambda correctly, but wraps it in a Convert call (why is that?). I wonder if casting the argument to the method call (a lambda) would finally give me the Expression> I need.
So the question is why the Convert call, and how to actually get the lambda with the Func signature.
I can't remember off the top of my head where I found this bit of code, but this is what I use to convert an Expr<'a -> 'b>
to Expression<Func<'a, 'b>>
. Hopefully this will solve your problem.
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)
One way you can now do this is to take advantage of the fact that F# will perform this conversion automatically when invoking methods on .NET types that expect a Expression<Func<...>>
.
I'm not entirely sure when this got added to the language, but certainly with F# 4, you don't need to explicitly convert F# expressions into LINQ ones. If the reason you wanted to do this in the first place was to be able to use IQueryable
LINQ APIs (or other expression-based .NET APIs) then it now just works with no effort, e.g.:
someEfDataContext.MyEntities.Single(fun e -> e.Id = 42)
just works. Even though this looks like an ordinary lambda (we've not used F#'s expression syntax), this compiles to code that produces an F# expression object, and then passes that to LeafExpressionConverter.QuotationToExpression
to turn it into a LINQ expression object.
But sometimes you'll want to get hold of the LINQ-style expression object directly in F#. (E.g., sometimes it's useful to write an F# function that produces an expression that you'll use in multiple queries.) In that case you can write a helper like this:
type FunAs() =
static member LinqExpression<'T, 'TResult>(e: Expression<Func<'T, 'TResult>>) = e
This looks like it does nothing - it just returns its argument. However, because FunAs
is a .NET type, F# will automatically compile any call site that invokes this with a fun
expression into code that generates a suitable LINQ query expression. E.g.:
let linqExpr = FunAs.LinqExpression(fun (e:MyEntity) -> e.Id = 42)
Here, linqExpr
will be of type Expression<Func<MyEntity, bool>>
.
The key to this is that this method is a member of a .NET Type. If you try the exact same thing with an ordinary F# function:
let funAsLinqExpression<'T, 'TResult>(e: Expression<Func<'T, 'TResult>>) = e
which seems like it should mean exactly the same thing as FunAs.LinqExpression
, you'll find that you can't call it in the same way. E.g., if you try this:
let linqExpr = funAsLinqExpression(fun (e:MyEntity) -> e.Id = 42)
You'll get a (slightly unhelpful) error: 'This function takes too many arguments, or is used in a context where a function is not expected`.
By making this function a member of a .NET type, we can take advantage of F#'s helpful "You seem to be invoking a .NET API that expects a LINQ-style expression, let me take care of that for you" feature.
(It's possible that there's some more explicit way of asking the LINQ compiler to perform this same trick for you without bringing a .NET type into the picture, but I've not found it.)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With