Search code examples
c#automapper

AutoMapper - Use mapping on profile


I am creating an AutoMapper configuration, where the structure does not match the destination in 1:1, I have to wrap my primary and secondary contact in the ContactResponse object. I cannot figure out how to use the mapping I created before to map the Contact object to a CommunicationContactResponse without redoing the config.

public class VendorResponseMappingProfile : Profile 
{
    public VendorResponseMappingProfile()
    {
        this.CreateMap<Contact, CommunicationContactResponse>()
            .ForMember(dest => dest.Phone, opt => opt.MapFrom(src => src.Phone))
            .ForMember(dest => dest.Email, opt => opt.MapFrom(src => src.Email));

        this.CreateMap<Vendor, VendorResponse>()
            .ForMember(dest => dest.id, opt => opt.MapFrom(src => src.Id))
            .ForMember(dest => dest.Name, opt => opt.MapFrom(src => src.Name))
            .ForMember(dest => dest.Memo, opt => opt.MapFrom(src => src.Memo))
            .ForMember(dest => dest.Active, opt => opt.MapFrom(src => src.Active))
            .ForMember(dest => dest.Contact, opt => opt.MapFrom(src => new ContactResponse
            {
                // Here i have to redo the mapping from above are there a way to use the mapping from above instead?
                PrimaryContact = src.PrimaryContact != null ? new CommunicationContactResponse
                {
                    Phone = src.PrimaryContact.Phone,
                    Email = src.PrimaryContact.Email
                } : null,
                SecondaryContact = src.SecondaryContact != null ? new CommunicationContactResponse
                {
                    Phone = src.SecondaryContact.Phone,
                    Email = src.SecondaryContact.Email
                } : null
            }));
    }
}

Solution

  • You need the Custom Value Resolver to use the mapping rule that has been registered for both PrimaryContact and SecondaryContact fields mapping from Contact to CommunicationContactResponse.

    While AutoMapper will automatically map the source and destination members if both have the same naming. Thus you don't need to specify the mapping rule for each member unless:

    Without extra configuration, AutoMapper requires a flattened destination to match the source type’s naming structure. When you want to project source values into a destination that does not exactly match the source structure, you must specify custom member mapping definitions.

    Solution 1:

    public class VendorResponseMappingProfile : Profile 
    {
    
        public VendorResponseMappingProfile()
        {
            this.CreateMap<Contact, CommunicationContactResponse>();
    
            this.CreateMap<Vendor, VendorResponse>()
                .ForMember(dest => dest.Contact, opt => opt.MapFrom((src, dest, srcMember, context) => new ContactResponse
                {
                    PrimaryContact = context.Mapper.Map<CommunicationContactResponse>(src.PrimaryContact),
                    SecondaryContact = context.Mapper.Map<CommunicationContactResponse>(src.SecondaryContact)
                }));
        }
    }
    

    (Updated) Solution 2:

    As suggested by @Lucian, a mapping from Vendor to ContactResponse is another option that is more simple.

    public class VendorResponseMappingProfile : Profile 
    {
    
        public VendorResponseMappingProfile()
        {
            this.CreateMap<Contact, CommunicationContactResponse>();
    
            this.CreateMap<Vendor, VendorResponse>()
                .ForMember(dest => dest.Contact, opt => opt.MapFrom(src => src));
    
            this.CreateMap<Vendor, ContactResponse>();
        }
    }