Search code examples
c#automapper

AutoMapper - How to map wrapped object with ConstructUsing


I am trying to unwrap a wrapped class to a flat object via AutoMapper.

I looked at similar questions such as AutoMapper c# runtime mapping depending on an object property, but I want the derived type to be mapped by AutoMapper, I do not want to return new MyFlatObject() in my AutoMapper config.

All the properties should be mapped via the Value child property, only IsDeleted is relevant from the parent.

What I have so far:

The error is:

Unmapped members were found. Review the types and members below. Add a custom mapping expression, ignore, add a custom resolver, or modify the source/destination type For no matching constructor, add a no-arg ctor, add optional arguments, or map all of the constructor parameters
=> MyObject -> MyFlatObject (Destination member list)

[TestClass]
public class AutoMappingTest
{
    public interface IMyObject { }

    public class DeletableWrapper
    {
        public bool IsDeleted { get; set; }
        public IMyObject Value { get; set; }
    }

    public class MyObject : IMyObject
    {
        public bool Foo { get; set; }
    }

    public class MyFlatObject
    {
        public bool IsDeleted { get; set; }
        public bool Foo { get; set; }
    }

    [TestMethod]
    public void MappingTest()
    {
        var config = new MapperConfiguration(cfg =>
        {
            cfg.CreateMap<DeletableWrapper, MyFlatObject>().ConstructUsing((src, ctx) => ctx.Mapper.Map<MyFlatObject>(src.Value))
                .ForMember(e => e.IsDeleted, opt => opt.MapFrom(s => s.IsDeleted));

            cfg.CreateMap<MyObject, MyFlatObject>();
        });

        config.AssertConfigurationIsValid();
    }
}

Solution

  • Note: My solution without ConstructUsing

    From config.AssertConfigurationIsValid, it complains that there is no mapping (rule) for the member IsDeleted in the (class) mapping rule from MyObject to MyFlatObject.

    You can apply Ignore() as there is no mapping from the source and thus it will not complain.

    For flattening, you can use IncludeMembers.

    var config = new MapperConfiguration((cfg) => 
    {
        cfg.CreateMap<AutoMappingTest.DeletableWrapper, AutoMappingTest.MyFlatObject>()
            .ForMember(dest => dest.IsDeleted, opt => opt.MapFrom(src => src.IsDeleted))
            .IncludeMembers(src => (AutoMappingTest.MyObject)src.Value);
    
        cfg.CreateMap<AutoMappingTest.MyObject, AutoMappingTest.MyFlatObject>()
            .ForMember(dest => dest.IsDeleted, opt => opt.Ignore());
    });
    

    As @Lucian's feedback, the .ForMember can be omitted as AutoMapper will map the members automatically when the members have the same name:

    cfg.CreateMap<AutoMappingTest.DeletableWrapper, AutoMappingTest.MyFlatObject>()
        .IncludeMembers(src => (AutoMappingTest.MyObject)src.Value);