Search code examples
c#asp.net-coreautomapper

AutoMapper - Issue with IValueConverter in ReverseMap


I am trying to map between two classes ParentA and ParentB using AutoMapper with a custom value converter. However, I am encountering an issue when mapping back from ParentB to ParentA.

We are using AutoMapper attributes to do the mapping.

Here is my code:

ParentA ssssssss = new ParentA { Id = 1, isActive = "Y", Name = "test" };
var response = automapper.Map<ParentB>(ssssssss);
response.Value = 500;
var yyyy = automapper.Map<ParentA>(response);

Class Definitions:

public class ParentA
{
    public int Id { get; set; }
    public string Name { get; set; }
    public string isActive { get; set; }
}

[AutoMap(typeof(ParentA), ReverseMap = true)]
public class ParentB
{
    public int Id { get; set; }
    public string Name { get; set; }

    [ValueConverter(typeof(IntConverter))]
    [SourceMember("isActive")]
    public int Value { get; set; } 
}

Converter:

public class IntConverter : IValueConverter<string, int>
{
    public int Convert(string sourceMember, ResolutionContext context)
    {
        if (sourceMember == "Y") return 100;
        if (sourceMember == "N") return 200;
        if (sourceMember == "P") return 500;
        return 1000;
    }
 
    public string Convert(int sourceMember, ResolutionContext context)
    {
        throw new NotImplementedException();
    }
}

The mapping from ParentA to ParentB works correctly.

When mapping back from ParentB to ParentA, it fails because the Convert method for the reverse mapping in IntConverter is not implemented.

Could you help me to implement the reverse mapping in IValueConverter?


Solution

  • After some trials and errors, I think that attribute-based mapping has some limitations when performing the reverse mapping with the value converter.

    The implementation of Value Converter supports only one-way mapping.

    Thus, would recommend removing the ReverseMap = true from the [AutoMap] attribute. And you should apply the [AutoMap] attribute to the ParentB class and [SourceMember] and [ValueConverter] attributes to the isActive property.

    [AutoMap(typeof(ParentB))]
    public class ParentA
    {
        public int Id { get; set; }
        public string Name { get; set; }
    
        [ValueConverter(typeof(StringConverter))]
        [SourceMember("Value")]
        public string isActive { get; set; }
    }
    
    [AutoMap(typeof(ParentA))]
    public class ParentB
    {
        public int Id { get; set; }
        public string Name { get; set; }
    
        [ValueConverter(typeof(IntConverter))]
        [SourceMember("isActive")]
        public int Value { get; set; }
    }
    
    public class StringConverter : IValueConverter<int, string>
    {
        public string Convert(int sourceMember, ResolutionContext context)
        {
            
            if (sourceMember == 100)
                return "Y";
            if (sourceMember == 200)
                return "N";
            if (sourceMember == 500)
                return "P";
            return null;
        }
    }
    

    Otherwise, you should work with Fluent configuration (Profile and/or MappingConfiguration) to provide the mapping rule for the reverse mapping for the Value/isActive property.

    public class MappingProfile : Profile
    {
        public MappingProfile()
        {
            CreateMap<ParentA, ParentB>()
                .ForMember(dest => dest.Value, opt => opt.ConvertUsing(new IntConverter(), src => src.isActive))
                .ReverseMap()
                .ForMember(dest => dest.isActive, opt => opt.ConvertUsing(new StringConverter(), src => src.Value));
        }
    }