Open TOTBWF opened 7 years ago
As a workaround, this code works, though it is pretty dirty
let rec private (|RecordReduction|_|) expr =
let rec traverse env = function
| Let(v,e,r) -> traverse (env |> Map.add v e) r
| NewRecord(ty, exprs) ->
let result =
exprs
|> List.map(fun e ->
match e with
| Var v -> Map.find v env
| _ -> e)
Some(Expr.NewRecord(ty, result))
| _ -> None
traverse Map.empty expr
type IQueryable<'TSource> with
// Here be dragons
member source.Update([<ReflectedDefinition>]selector: Expr<'TSource -> 'TSource> ) =
let expr =
match selector with
| Lambda(v, RecordReduction(record)) -> Expr.Lambda(v, record) |> Expr.Cast<'TSource -> 'TSource>
| e -> e
let linq = LeafExpressionConverter.QuotationToExpression expr
let call = linq :?> MethodCallExpression
let lambda = call.Arguments.[0] :?> LambdaExpression
let generateNewExpression () =
let body = lambda.Body :?> NewExpression
let ctor = typeof<NewExpression>.GetConstructors(BindingFlags.NonPublic ||| BindingFlags.Instance).[0]
let members = FSharpType.GetRecordFields(typeof<'TSource>) |> Seq.map(fun field -> field :> MemberInfo) |> ResizeArray |> ReadOnlyCollection
ctor.Invoke([| body.Constructor; body.Arguments; members |]) :?> NewExpression
let boxedSelector = Expression.Lambda<Func<'TSource, obj>>(generateNewExpression(), lambda.Parameters)
QueryableExtensions.Update(source, boxedSelector)
F# has no implicit upcasting, requiring either a direct cast using
:>
or theupcast
keyword. This causes issues when trying to use the Linq update, as it requires aFunc<TSource, Object>
, and calls to:>
andupcast
count as method invocation, meaning there is actually no way to callUpdate
from F#.