F# - "不是有效的属性表达式"

时间:2011-04-25 07:41:05

标签: 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>,这似乎可以正常工作,但是当它到达将它转换为LINQ的行时(需要做)这是因为cfg.HasMany除了System.Expression<Func<'a,ICollection<'b>>>之外,它只会抛出一个异常说:

  

InvalidOperationException异常:

     

表达式'z =&gt;   UnboxGeneric(ToFSharpFunc(z =&gt;   z.Books).Invoke(z))'不是有效的   财产表达。表达方式   应该代表一个属性:C#:'t =&gt;   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 - 但当然这只是一个理论,而我根本不知道该怎么做,以便解决它。

1 个答案:

答案 0 :(得分:2)

你的推理是正确的 - 问题是HasMany方法只识别特定的C#表达式树,而你的F#代码生成的表达式树是不同的。

我的猜测是EF只处理表达式树是对正确类型的属性的普通访问的情况 - 在C#语法中类似于:x => x.Foo(没有任何强制转换等)。我认为最好的选择是修改代码以期望函数'a -> ICollection<'b>

如果你有办法建立一个正确的表达式树 - 例如如果用户指定x => x.Foo,您想要返回x => x.FooInternal,那么您可以使用模式&amp;使用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期望的结构相匹配。我想用一些其他属性(例如一些具有正确类型的内部版本)替换用户指定的实际属性几乎是你唯一能做的事情。