2011-04-25 121 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

相關問題