Search code examples
c#automapper

Automapper doesn't convert objects correctly with nested type with same propertie's names


I'm trying to map an object of type A with nested object of type NestedType to an object of type B using AutoMapper, but I've encountered some challenges: If we have same name for:

  1. propertie with type of nested class in class A (source)
  2. string property in class B (result)

Scenario:

We have these types:

  • Source type A:
public class A
{
    public NestedClass AnyName { get; set; }
    // Other properties
    // ...
}
  • Nested type NestedClass:
public class NestedClass
{
    public string Parameter { get; init; }
    // Other properties
    // ...
}
  • Source type B
public class B
{
    public string AnyName { get; set; }
    // Other properties
    // ...
}

And I get an unexpected result when I try to convert like this:

public class MainProfile : Profile
{
    public MainProfile()
    {
        CreateMap<A, B>(MemberList.None)
            .ConstructUsing(x => ConvertToB(x))
            ;
    }

    private B ConvertToB(A a)
    {
        return new B
        {
            AnyName = a.AnyName.Parameter,
            // Other properties
            // ...
        };
    }
}

Example:

For example, I have instance of A:

using AutoMapper;

var a = new A()
{
    AnyName = new() { Parameter = "123" }
};

var configuration = new MapperConfiguration(cfg =>
{
    cfg.AddProfile<MainProfile>();
});

IMapper mapper = configuration.CreateMapper();

var b = mapper.Map<B>(a);

Console.WriteLine(b.AnyName);

Expected result:

123

Actual result:

NestedClass

Solution

  • It looks like you're trying to flatten the nested type as part of your mapping. AutoMapper will do this automatically if you name your properties in a certain way, without the need for your ConvertToB method.

    using System;
    using AutoMapper;
                        
    public class Program
    {
        public static void Main()
        {
            var a = new A()
            {
                AnyName = new() { Parameter = "123" }
            };
    
            var configuration = new MapperConfiguration(cfg =>
            {
                cfg.AddProfile<MainProfile>();
            });
    
            IMapper mapper = configuration.CreateMapper();
    
            var b = mapper.Map<B>(a);
    
            Console.WriteLine(b.AnyNameParameter);
        }
    }
    
    public class A
    {
        public NestedClass AnyName { get; set; }
    }
    
    public class B
    {
        public string AnyNameParameter { get; set; }
    }
    
    public class NestedClass
    {
        public string Parameter { get; init; }
    }
    
    public class MainProfile : Profile
    {
        public MainProfile()
        {
            CreateMap<A, B>();
        }
    }
    

    https://dotnetfiddle.net/34v1SA

    AutoMapper does that by convention, but if you don't want to change the parameter name you can use ForMember and MapFrom which gives you some more control over your mappings if needed.

    public MainProfile()
    {
        CreateMap<A, B>()
            .ForMember(d => d.Parameter, opt => opt.MapFrom(s => s.AnyName.Parameter));
    }
    

    https://docs.automapper.org/en/latest/Flattening.html