Q:

AutoMapper customization - Mapping strings to DTO

I have the following models:
public class AccountDto
{
    public Guid Id { get; set; }
    public string ChamberOfCommerce { get; set; }
    public string Code { get; set; }
    public VatDto PurchaseVatCode { get; set; }
    public VatDto SalesVatCode { get; set; }
}

public class VatDto
{
    public string Code { get; set; }
    public string Description { get; set; }
    public double Percentage { get; set; }
}

public class Account
{
    public Guid Id { get; set; }
    public string ChamberOfCommerce { get; set; }
    public string Code { get; set; }

    public string PurchaseVatCode { get; set; }
    public string PurchaseVatDescription { get; set; }
    public double PurchaseVatPercentage { get; set; }

    public string SalesVatCode { get; set; }
    public string SalesVatDescription { get; set; }
    public double SalesVatPercentage { get; set; }
}

I want to read the data in flat mode and then map it to the AccountDto. 
I tried to configure the AutoMapper using following lines:
var config = new MapperConfiguration(cfg => {
    cfg.AddMemberConfiguration()
       .AddMember<NameSplitMember>()
       .AddName<PrePostfixName>(_ => _.AddStrings(p => p.Prefixes, "Purchase", "Sales"))
       .AddName<ReplaceName>(_ => _.AddReplace("Purchase", string.Empty).AddReplace("Sales", string.Empty));
    cfg.RecognizePrefixes("Purchase");
    cfg.RecognizePrefixes("Sales");
    cfg.RecognizeDestinationPrefixes("Purchase");
    cfg.RecognizeDestinationPrefixes("Sales");
    cfg.CreateMap<AccountDto, Models.Account>().ReverseMap();
});

var mapper = config.CreateMapper();
var dto = mapper.Map<AccountDto>(account);

But none of the above attempts worked and I still can't map the PurchaseVatCode.
I get the following error:
Property:
PurchaseVatCode ---> AutoMapper.AutoMapperConfigurationException:
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
String -> VatDto (Destination member list)
System.String -> Online.Account.Domain.Dto.VatDto (Destination member list)

Unmapped properties:
Code
Description
TranslatedDescription
Percentage

Can anyone help with this issue? 
Thanks in advance!

A:

All the difficulties originate from the very bad choice of AccountDto property names PurchaseVatCode and SalesVatCode. In order to let AutoMapper correctly flatten the names, we need to strip Code suffix.  
The problem is that the Account has properties with the same name but different type. So we can't use RecognizeAlias to rename them because it will rename the target property as well.  
The only methods that are separate for source and target are for specifying prefix/suffix. But if we use RecognizeSuffix("Code") in order to strip Code from source PurchaseVatCode and SalesVatCode, that unfortunately affects the VatDto.Code property as well (this probably can be considered a current AutoMapper defect).
The best you can do in this scenario is to use the later to do the most of the job and explicitly map the Code properties:
var config = new MapperConfiguration(cfg =>
{
    cfg.RecognizePostfixes("Code");

    cfg.CreateMap<AccountDto, Account>()
        .ForMember(dst => dst.PurchaseVatCode, opt => opt.MapFrom(src => src.PurchaseVatCode.Code))
        .ForMember(dst => dst.SalesVatCode, opt => opt.MapFrom(src => src.SalesVatCode.Code))
        .ReverseMap();
});

