Usando AutoMapper con F #

Estoy tratando de usarAutoMapper de F #, pero tengo problemas para configurarlo debido al uso intensivo de LINQ Expressions por parte de AutoMapper.

Específicamente, el tipo AutoMapperIMappingExpression<'source, 'dest> Tiene un método con esta firma:

ForMember(destMember: Expression<Func<'dest, obj>>, memberOpts: Action<IMemberConfigurationExpression<'source>>)

Esto se usa típicamente en C # como esto:

Mapper.CreateMap<Post, PostsViewModel.PostSummary>()
    .ForMember(x => x.Slug, o => o.MapFrom(m => SlugConverter.TitleToSlug(m.Title)))
    .ForMember(x => x.Author, o => o.Ignore())
    .ForMember(x => x.PublishedAt, o => o.MapFrom(m => m.PublishAt));

Hice una envoltura F # que organiza las cosas para que la inferencia de tipos pueda funcionar. Esta envoltura me permite traducir el ejemplo de C # anterior en algo como esto:

Mapper.CreateMap<Post, Posts.PostSummary>()
|> mapMember <@ fun x -> x.Slug @> <@ fun m -> SlugConverter.TitleToSlug(m.Title) @>
|> ignoreMember <@ fun x -> x.Author @>
|> mapMember <@ fun x -> x.PublishedAt @> <@ fun m -> m.PublishAt @>
|> ignore

Este código se compila, y parece bastante limpio en cuanto a sintaxis y uso. Sin embargo, en tiempo de ejecución, AutoMapper me dice esto:

AutoMapper.AutoMapperConfigurationException: la configuración personalizada para miembros solo se admite para miembros individuales de nivel superior en un tipo.

Supongo que esto se debe al hecho de que tengo que convertirExpr<'a -> 'b> dentroExpression<Func<'a, obj>>. Yo convierto el'b aobj con un cast, lo que significa que mi expresión lambda ya no es simplemente un acceso de propiedad. Recibo el mismo error si escribo el valor de la propiedad en la oferta original y no hago ningún empalme dentroforMember (vea abajo). Sin embargo, si no encierro el valor de la propiedad, termino conExpression<Func<'a, 'b>> que no coincide con el tipo de parámetro queForMember espera,Expression<Func<'a, obj>>.

Creo que esto funcionaría si AutoMapper'sForMember era completamente genérico, pero forzando el tipo de retorno de la expresión de acceso de miembro a serobj significa que solo puedo usarlo en F # para propiedades que ya son directamente de tipoobj y no una subclase. Siempre puedo recurrir al uso de la sobrecarga deForMember eso toma el nombre del miembro como una cadena, pero pensé que me gustaría verificar si alguien tiene una solución alternativa brillante antes de renunciar a la corrección de errores de tiempo de compilación.

Estoy usando este código (más la parte LINQ de F # PowerPack) para convertir una cita de F # en una expresión LINQ:

namespace Microsoft.FSharp.Quotations

module Expr =
    open System
    open System.Linq.Expressions
    open Microsoft.FSharp.Linq.QuotationEvaluation

    // http://stackoverflow.com/questions/10647198/how-to-convert-expra-b-to-expressionfunca-obj
    let ToFuncExpression (expr:Expr<'a -> 'b>) =
        let call = expr.ToLinqExpression() :?> MethodCallExpression
        let lambda = call.Arguments.[0] :?> LambdaExpression
        Expression.Lambda<Func<'a, 'b>>(lambda.Body, lambda.Parameters) 

Esta es la envoltura real de F # para AutoMapper:

namespace AutoMapper

/// Functions for working with AutoMapper using F# quotations,
/// in a manner that is compatible with F# type-inference.
module AutoMap =
    open System
    open Microsoft.FSharp.Quotations

    let forMember (destMember: Expr<'dest -> 'mbr>) (memberOpts: IMemberConfigurationExpression<'source> -> unit) (map: IMappingExpression<'source, 'dest>) =
        map.ForMember(Expr.ToFuncExpression <@ fun dest -> ((%destMember) dest) :> obj @>, memberOpts)

    let mapMember destMember (sourceMap:Expr<'source -> 'mapped>) =
        forMember destMember (fun o -> o.MapFrom(Expr.ToFuncExpression sourceMap))

    let ignoreMember destMember =
        forMember destMember (fun o -> o.Ignore())
Actualizar:

Pude usarCódigo de muestra de tomas para escribir esta función, que produce una expresión con la que se satisface AutoMapper para el primer argumentoIMappingExpression.ForMember.

let toAutoMapperGet (expr:Expr<'a -> 'b>) =
    match expr with
    | Patterns.Lambda(v, body) ->
        // Build LINQ style lambda expression
        let bodyExpr = Expression.Convert(translateSimpleExpr body, typeof<obj>)
        let paramExpr = Expression.Parameter(v.Type, v.Name)
        Expression.Lambda<Func<'a, obj>>(bodyExpr, paramExpr)
    | _ -> failwith "not supported"

Todavía necesito el soporte de PowerPack LINQ para implementar mimapMember Funcionan, pero ambos funcionan ahora.

Si alguien está interesado, puede encontrar elcódigo completo aquí.

Respuestas a la pregunta(2)

Su respuesta a la pregunta