F#-“不是有效的属性表达式"; [英] F# - "Not a valid property expression"

查看:99
本文介绍了F#-“不是有效的属性表达式";的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在使用一种以Expr作为参数的方法:

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

我想要的是将'b seq强制转换为ICollection<'b>,这似乎应该可以正常工作,但是当它到达将要把Expr转换为LINQ的那一行时(由于System.Expression<Func<'a,ICollection<'b>>>之外)只是抛出一个异常:

InvalidOperationException:

表达式'z => UnboxGeneric(ToFSharpFunc(z => z.Books..Invoke(z))'不是有效的 属性表达式.表达方式 应该代表一个属性:C#:'t => t.MyProperty'VB.Net:'函数(t) t.MyProperty".

我用于将Expr转换为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) 

我以前使用toLinq函数没有问题-我认为这是因为我将b seq强制转换为ICollection<'b>,这将UnboxGeneric保留在Expr中,并且将Expr传递给toLinq它只是简单地知道如何使用UnboxGeneric-但是那当然只是一个理论,我根本不知道该怎么做才能解决它.

解决方案

您的推理是正确的-问题是HasMany方法仅识别特定的C#表达式树,而您的F#代码生成的表达式树是不同的. /p>

我的猜测是,当表达式树是对正确类型的属性的简单访问时,EF仅会处理这种情况-在C#语法中类似:x => x.Foo(不进行任何强制转换等).我认为最好的选择是修改您的代码,使其也期望具有功能'a -> ICollection<'b>.

如果您有某种方法可以构建正确的表达式树-例如如果用户指定x => x.Foo,则要返回x => x.FooInternal,则可以使用模式&用F#引号重建表达式树的函数:

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!"

...,但请记住,结果必须与HasMany预期的结构匹配.我猜想您只能用其他一些属性(例如,具有正确类型的内部版本)替换用户指定的实际属性.

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.

解决方案

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.

这篇关于F#-“不是有效的属性表达式";的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆