2011-04-25 152 views
1

我有这种方法,这需要Expr的作为参数:F# - “不是有效的属性表达”

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线(需要这样做,因为cfg.HasMany节选一System.Expression<Func<'a,ICollection<'b>>>)它只是抛出一个异常说:

出现InvalidOperationException:

表达 'Z => UnboxGeneric(ToFSharpFunc(Z => z.Books).Invoke(Z))' 是不是一个有效 属性表达。表达式 应表示一个属性:C#:'t => t.MyProperty'VB.Net:'Function(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 seqICollection<'b>这让在ExprUnboxGeneric和当通过ExprtoLinq它简直不知道该怎么处理UnboxGeneric - 但当然这只是一个理论,我不知道该怎么做才能解决它。

回答

2

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

我的猜测是,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。我想用其他属性替换用户指定的实际属性(例如某些内部版本具有正确的类型)几乎是你可以做的唯一的事情。

+0

最后一种方法看起来很有趣,但我不确定如何混合'Expr.Lambda'和'Expr.PropGet'来构建表达式树。你介意给出一个解释; =)? – ebb 2011-04-25 13:40:16

+0

如果我让函数期望''a - > ICollection <'b>',那么当你调用函数时,你只需要抛出'seq <'b>',这会产生相同的错误。 – ebb 2011-04-25 13:45:36

相关问题