ebb
ebb

Reputation: 9377

query function with predicate

Mjello there! I'm at the moment trying to create a F# application, and now I've hit another wall.

The problem lays in the Microsoft.FSharp.Linq.Query module, the query function in there wont accept my predicates if they're passed as parameters. Check this out:

member x.GetUsersWhere (e:User -> bool) =
    query <@ ctx.Users |> Seq.filter e @>

and then I call it like:

let service = new UserService()
service.GetUsersWhere (fun z -> z.Name = "James")

That should be fine eh? Well, the compiler dosent agree:

The following construct was used in query but is not recognised by the F#-to-LINQ query translator: Call (None, System.Collections.Generic.IEnumerable1[WebFSharp.Entities.User] op_PipeRight[DbSet1,IEnumerable1](System.Data.Entity.DbSet1[WebFSharp.Entities.User], Microsoft.FSharp.Core.FSharpFunc2[System.Data.Entity.DbSet1[WebFSharp.Entities.User],System.Collections.Generic.IEnumerable1[WebFSharp.Entities.User]]), [PropertyGet (Some (FieldGet (Some (Value (WebFSharp.Business.UserService)), WebFSharp.Business.MyContext ctx)), System.Data.Entity.DbSet1[WebFSharp.Entities.User] Users, []), Let (predicate, Value (), Lambda (source, Call (None, System.Collections.Generic.IEnumerable1[WebFSharp.Entities.User] Filter[User](Microsoft.FSharp.Core.FSharpFunc2[WebFSharp.Entities.User,System.Boolean], System.Collections.Generic.IEnumerable1[WebFSharp.Entities.User]), [predicate, Coerce (source, System.Collections.Generic.IEnumerable1[[WebFSharp.Entities.User, WebFSharp.Entities, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null]])])))]) This is not a valid query expression. Check the specification of permitted queries and consider moving some of the query out of the quotation

Whats going on? Is it because the Expr will be evaluated before the parameter is injected into it?

Anyway, the following code works but is much less flexible:

member x.GetUsersByName name =
    query <@ ctx.Users |> Seq.filter(fun z -> z.Name = name) @>

let service = new UserService()
service.GetUsersByName "James"

Could anyone please shed some light on what's happening?

Upvotes: 1

Views: 1470

Answers (1)

Tomas Petricek
Tomas Petricek

Reputation: 243051

In your code, the F# to LINQ translator doesn't have any way to examine the function that you specified (the argument e is some compiled code - there is no way to get its quoted representation that could be translated to SQL).

You need to use Expr<User -> bool> instead of User -> bool. This means that instead of passing a function, you'll pass some representation of the code used as argument (which can be analyzed & translated to SQL):

member x.GetUsersWhere (e:Expr<User -> bool>) = 
    query <@ ctx.Users |> Seq.filter %e @>

Note the %e syntax - it means that the quoted code should be embedded (spliced) into the main quoted expression, so the translator sees it as a single expression. When calling it, you need to pass the function using quotations too:

let service = new UserService()service.GetUsersWhere <@ fun z -> z.Name = "James" @>

Upvotes: 8

Related Questions