Using AutoMapper with F#

前端 未结 2 683
小蘑菇
小蘑菇 2021-02-15 05:31

I\'m trying to use AutoMapper from F#, but I\'m having trouble setting it up due to AutoMapper\'s heavy use of LINQ Expressions.

Specifically, the AutoMapper type

相关标签:
2条回答
  • 2021-02-15 05:51

    I'm not quite sure how to fix the generated expression tree (that's doable by post-processing it, but it is pain to find out what AutoMapper expects). However, there are two alternatives:

    As a first option - the expressions that you need to translate are fairly simple. They are mostly just method calls, property getters and uses of a variable. This means that it should be possible to write your own quotation to expression trees translator that produces exactly the code you want (then you can also add your own handling of obj, perhaps by calling Expression.Convert to build the expression tree). I wrote a simple quotation tranlsator as a sample, which should handle most of the stuff in your sample.

    As a second option - if AutoMapper provides an option to specify just a property name - you could just use quotations of the form <@ x.FooBar @>. These should be quite easy to deconstruct using the Patterns.PropertyGet pattern. The API should maybe look like this:

    Mapper.CreateMap<Post, Posts.PostSummary>(fun post summary mapper ->
      mapper |> mapMember <@ post.Slug @> // not sure what the second argument should be?
             |> ignoreMember <@ post.Author @> )
    

    Or, in fact, you could use this style of API even in the first case, because you don't need to write lambda expressions repeatedly for every single mapping, so maybe it is a bit nicer :-)

    0 讨论(0)
  • 2021-02-15 05:55

    Now that F# is happy to generate a Expression<Func<...>> directly from a fun expression, this is relatively easy to solve. The biggest problem now is that the F# compiler seems to get confused by the overloading of the ForMember method, and is unable to infer correctly what you want. This can be circumvented by defining an extension method with a different name:

    type AutoMapper.IMappingExpression<'TSource, 'TDestination> with
        // The overloads in AutoMapper's ForMember method seem to confuse
        // F#'s type inference, forcing you to supply explicit type annotations
        // for pretty much everything to get it to compile. By simply supplying
        // a different name, 
        member this.ForMemberFs<'TMember>
                (destGetter:Expression<Func<'TDestination, 'TMember>>,
                 sourceGetter:Action<IMemberConfigurationExpression<'TSource, 'TDestination, 'TMember>>) =
            this.ForMember(destGetter, sourceGetter)
    

    You can then use the ForMemberFs method more or less as the original ForMember is intended to work, e.g.:

    this.CreateMap<Post, Posts.PostSummary>()
        .ForMemberFs
            ((fun d -> d.Slug),
             (fun opts -> opts.MapFrom(fun m -> SlugConverter.TitleToSlug(m.Title)))
    
    0 讨论(0)
提交回复
热议问题