Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to use AutoMapper to map an object with multiple generic lists to another object with corresponding non-generic lists?

Tags:

f#

automapper

I have created a ListTypeConverter:

type ListTypeConverter<'source, 'destination>() =
    interface ITypeConverter<'source list, Proxies.List> with
        member this.Convert(source, destination, context) =
            let proxyList = new Proxies.List()
            source 
            |> List.map(fun item -> _mapper.Map<'source, 'destination>(item))
            |> List.iter(fun item -> proxyList.addEnd(item) |> ignore)
            proxyList

And usage: ForMemberFs by Ian Griffiths

this.CreateMap<SourceItemType list, Proxies.List>().ConvertUsing<ListTypeConverter<SourceItemType, DestItemType>>()

this.CreateMap<SourceType, DestType>().
    ForMemberFs((fun d -> d.MyNonGenericList), (fun opts -> opts.MapFrom(fun s -> s.MyGenericList))).

This works fine if I only have one property on my primary map that maps from a 'a list -> Proxy.List. But the moment I introduce a second mapping from a 'b -> Proxy.List then I get an InvalidCastException.

Introducing a second mapping causes the exception:

this.CreateMap<SourceItemType list, Proxies.List>().ConvertUsing<ListTypeConverter<SourceItemType, DestItemType>>()
this.CreateMap<SourceItemType2 list, Proxies.List>().ConvertUsing<ListTypeConverter<SourceItemType2, DestItemType2>>()

this.CreateMap<SourceType, DestType>().
    ForMemberFs((fun d -> d.MyNonGenericList), (fun opts -> opts.MapFrom(fun s -> s.MyGenericList))).
    ForMemberFs((fun d -> d.MyNonGenericList2), (fun opts -> opts.MapFrom(fun s -> s.MyGenericList2))).

Exception:

Unable to cast object of type 'obj' to type 'DestItemType'
like image 991
Aaron Palmer Avatar asked Oct 12 '16 19:10

Aaron Palmer


1 Answers

So, my problem is a bit deeper than just F# and AutoMapper. The Proxies.List type is actually gotten via a TypeProvider for Microsoft Dynamics AX that we've written in-house. I wanted to exclude that variable from the equation, so I wrote a simple script using System.Collections.Generic.List<'t> -> System.Collections.ArrayList (which is non-generic) and I was able to successfully cast from items in my ArrayList back to their correct "destination" type.

While I'm here, we came up with some pretty cool F# helpers for AutoMapper:

type OptionExpressions =
    static member MapFrom<'source, 'destination, 'sourceMember, 'destinationMember> (e: 'source -> 'sourceMember) =
        System.Action<IMemberConfigurationExpression<'source, 'destination, 'destinationMember>> (fun (opts: IMemberConfigurationExpression<'source, 'destination, 'destinationMember>) -> opts.MapFrom(e))
    static member UseValue<'source, 'destination, 'value> (e: 'value) =
        System.Action<IMemberConfigurationExpression<'source, 'destination, 'value>> (fun (opts: IMemberConfigurationExpression<'source, 'destination, 'value>) -> opts.UseValue(e))
    static member Ignore<'source, 'destination, 'destinationMember> () =
        System.Action<IMemberConfigurationExpression<'source, 'destination, 'destinationMember>> (fun (opts: IMemberConfigurationExpression<'source, 'destination, 'destinationMember>) -> opts.Ignore())

Usage:

// Shortened local helpers
let mapFrom = OptionExpressions.MapFrom
let ignoreMap = OptionExpressions.Ignore
let useValue = OptionExpressions.UseValue

this.CreateMap<Source, Destination>()
    .ForMemberFs((fun d -> d.DestMember1),  mapFrom (fun s -> s.SourceMember1))
    .ForMemberFs((fun d -> d.DestMember2),  useValue (MyValue))
    .ForMemberFs((fun d -> d.DestMember3),  ignoreMap ())
like image 86
Aaron Palmer Avatar answered Oct 27 '22 22:10

Aaron Palmer