F# - “Not a valid property expression”

只愿长相守 提交于 2019-12-08 07:28:53

问题


I'm having this method which takes a Expr as parameter:

member x.HasSeq (expr:Expr<'a -> 'b seq>) = 
    let casted = <@ fun z -> (%expr) z :?> ICollection<'b> @>
    ManyNavPropertyInfo(cfg.HasMany <| toLinq casted)

What I want is to cast the 'b seq to an ICollection<'b>, which seems to work as it should, however when it reaches the line where it's going to convert the Expr to LINQ (need to do this since cfg.HasMany excepts a System.Expression<Func<'a,ICollection<'b>>>) it simply throws an exception saying:

InvalidOperationException:

The expression 'z => UnboxGeneric(ToFSharpFunc(z => z.Books).Invoke(z))' is not a valid property expression. The expression should represent a property: C#: 't => t.MyProperty' VB.Net: 'Function(t) t.MyProperty'.

The function I use for converting Expr to LINQ:

let toLinq (exp : Expr<'a -> 'b>) =
    let linq = exp.ToLinqExpression()
    let call = linq :?> MethodCallExpression
    let lambda = call.Arguments.[0] :?> LambdaExpression
    Expression.Lambda<Func<'a, 'b>>(lambda.Body, lambda.Parameters) 

I've used the toLinq function before without problems - I think it's because I cast b seq to ICollection<'b> which leaves UnboxGeneric in the Expr and when passing the Expr to toLinq it simply dosent know what to do with the UnboxGeneric - but of course thats just a theory, and I dont know what to do at all, in order to resolve it.


回答1:


Your reasoning is correct - the problem is that the HasMany method recognizes only specific C# expression trees and the expression tree that your F# code generates is different.

My guess is that EF only handles a case when the expression tree is a plain access to a property of the right type - in the C# syntax something like: x => x.Foo (without any casting etc.). I think that the best option would be to modify your code to also expect a function 'a -> ICollection<'b>.

If you have some way for building a correct expression tree - e.g. if user specifies x => x.Foo, you want to return x => x.FooInternal, then you can use patterns & functions for working with F# quotations to rebuild the expression tree:

let hasSeq (e:Expr<'a -> seq<'b>>) =
  match e with
  | Patterns.Lambda(v, Patterns.PropertyGet(Some instance, propInfo, [])) ->
      printfn "Get property %s of %A" propInfo.Name instance
      // TODO: Use 'Expr.Lambda' & 'Expr.PropGet' to construct
      // an expression tree in the expected format
  | _ -> failwith "Not a lambda!"

... but keep in mind that the result needs to match the structure expected by HasMany. I guess that replacing the actual property specified by the user with some other property (e.g. some internal version that has the right type) is pretty much the only thing you can do.



来源:https://stackoverflow.com/questions/5776307/f-not-a-valid-property-expression

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!