Search code examples
c#.netautomapper.net-4.6.2

Mapping from type object to a custom class using AutoMapper 6.1.1 in .NET 4.6.2


My class XmlOrder contains the two properties

public class XmlOrder
{
    public OrderType OrderType { get; set; }
    public object Item { get; set; }
}

If OrderType equals OrderType.Service, Item will be of type XmlTransportElement. If it is OrderType.Transport, it will be of type XmlTransport. XmlTransport has the following two properties:

public class XmlTransport
{
    public XmlTransportElement Delivery { get; set; }
    public XmlTransportElement Pickup { get; set; }
}

XmlOrder needs to be mapped to Order which looks like this:

 public class Order : IOrder
 {
     public ITransportElement[] TransportElements { get; set; }
 }

This array will either have one or two entries based on the logic above.

In my sample project, I've written this using the latest AutoMapper version, and it works perfectly:

public class OrderProfile : Profile
{
    public OrderProfile()
    {            
        // Mapping for XmlOrder to IOrder
        CreateMap<XmlOrder, IOrder>()
            .ConstructUsing(src => new Order())
            .ForMember(dest => dest.TransportElements, opt => opt.MapFrom((src, dest, _, context) =>
                src.Item is XmlTransport transport
                    ? new[]
                    {
                        transport.Delivery != null ? context.Mapper.Map<TransportElement>(transport.Delivery) : null,
                        transport.Pickup != null ? context.Mapper.Map<TransportElement>(transport.Pickup) : null
                    }
                    : new[] { context.Mapper.Map<TransportElement>((XmlTransportElement)src.Item) }
            ));

        // Mapping for XmlTransportElement to TransportElement
        CreateMap<XmlTransportElement, TransportElement>()
             // additional logic
    }
}

The problem is, I need to implement this in a legacy application using AutoMapper 6.1.1. The above code doesn't work because .MapFrom cannot be used like this in older versions. I've been going in circles for two days and tried various approaches using .ConvertUsing and .ResolveUsing in combination with a custom converter but always get NullReferenceExceptions or other errors.

The latest approach I've tried was this one, which throws an AutoMapperMappingException:

 CreateMap<XmlOrder, IOrder>()
        .ForMember(dest => dest.TransportElements, opt => opt.ResolveUsing(src =>
        {
            var transport = src.Item as Transport;
            if (transport != null)
            {
                return new[]
                {
                    transport.Delivery != null ? Mapper.Map<TransportElement>(transport.Delivery) : null,
                    transport.Pickup != null ? Mapper.Map<TransportElement>(transport.Pickup) : null
                };
            }
            return new[] { Mapper.Map<TransportElement>((XmlTransportElement)src.Item) };
        }))
        .ConstructUsing(src => new Order());

Any help is appreciated. AI hasn't been helpful at all and keeps suggesting things that don't work with this particular AutoMapper version.


Solution

  • Found a solution eventually.

    CreateMap<XmlOrder, IOrder>( )
        .ConstructUsing(src => new Order( ))
        .ForMember(dest => dest.TransportElements, opt => opt.ResolveUsing(src => {
            switch (src.Item)
            {
                case XmlTransport xmltransport:
                {
                    var elements = new List<TransportElement>( );
    
                    if ( transport.Delivery != null ) {
                        elements.Add(new TransportElement {
                            // additional logic
                        });
                    }
    
                    if ( transport.Pickup != null ) {
                        elements.Add(new TransportElement {
                            // additional logic
                        });
                    }
    
                    return elements.ToArray( );
                }
                case XmlTransportElement xmlTransportElement:
                    return new[]
                    {
                        new TransportElement
                        {
                            // additional logic
                    };
                default:
                    return null;
            }
        }));