Używanie AutoMappera z F #

Próbuję użyćAutoMapper z F #, ale mam problem z ustawieniem go z powodu intensywnego używania wyrażeń LINQ przez AutoMapper.

W szczególności typu AutoMapperIMappingExpression<'source, 'dest>&nbsp;ma metodę z tym podpisem:

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

Zwykle jest to używane w C # w ten sposób:

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));

Zrobiłem opakowanie F #, które porządkuje rzeczy, aby wnioskowanie typu mogło działać. Ten wrapper pozwala mi przetłumaczyć powyższy przykład C na coś takiego:

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

Ten kod się kompiluje i wydaje się całkiem czysty, jeśli chodzi o składnię i użycie. Jednak w czasie wykonywania AutoMapper mówi mi o tym:

AutoMapper.AutoMapperConfigurationException: konfiguracja niestandardowa dla członków jest obsługiwana tylko dla pojedynczych członków najwyższego poziomu typu.

Przypuszczam, że jest to spowodowane faktem, że muszę się nawrócićExpr<'a -> 'b>&nbsp;wExpression<Func<'a, obj>>. Konwertuję'b&nbsp;doobj&nbsp;z obsadą, co oznacza, że ​​moje wyrażenie lambda nie jest już po prostu dostępem do własności. Otrzymuję ten sam błąd, jeśli zaznaczę wartość właściwości w oryginalnym cytacie i nie wykonam żadnego splicingu w środkuforMember&nbsp;(patrz poniżej). Jeśli jednak nie ustawię wartości nieruchomości, otrzymamExpression<Func<'a, 'b>>&nbsp;który nie pasuje do typu parametruForMember&nbsp;oczekuje,Expression<Func<'a, obj>>.

Myślę, że to zadziałałoby, gdyby AutoMapperaForMember&nbsp;była całkowicie ogólna, ale wymuszanie typu powrotu wyrażenia dostępu członka byłoobj&nbsp;oznacza, że ​​mogę używać go tylko w F # dla właściwości, które są już bezpośrednio typuobj&nbsp;a nie podklasa. Zawsze mogę skorzystać z przeciążeniaForMember&nbsp;który przyjmuje nazwę członka jako ciąg, ale pomyślałem, że sprawdzę, czy ktoś ma genialne obejście, zanim zrezygnuję z sprawdzania literówki podczas kompilacji.

Używam tego kodu (plus części LINQ F # PowerPack), aby przekonwertować cytat F # na wyrażenie 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) 

To jest rzeczywiste opakowanie F # dla AutoMappera:

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())
Aktualizacja:

Byłem w stanie użyćPrzykładowy kod Tomasa&nbsp;napisać tę funkcję, która tworzy wyrażenie, z którego AutoMapper jest zadowolony do pierwszego argumentuIMappingExpression.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"

Nadal potrzebuję wsparcia PowerPack LINQ w celu wdrożenia mojegomapMember&nbsp;funkcja, ale obie działają teraz.

Jeśli ktoś jest zainteresowany, może znaleźćpełny kod tutaj.