I have a DTO object with a lower-underscore naming convention, which I would like to map to and from a DB entity with a Pascal case naming convention. I'm having issues mapping properties with numbers in the name. Here's a minimum reproducible example:
using AutoMapper;
MapperConfiguration config =
new(cfg =>
{
cfg.SourceMemberNamingConvention = PascalCaseNamingConvention.Instance;
cfg.DestinationMemberNamingConvention = LowerUnderscoreNamingConvention.Instance;
cfg.CreateMap<MyDbEntity, MyDto>().ReverseMap();
});
config.AssertConfigurationIsValid();
IMapper mapper = config.CreateMapper();
// dto.equip_crest_slot_type_1_crest_id_1 = 10
MyDto dto = mapper.Map<MyDto>(new MyDbEntity() { EquipCrestSlotType1CrestId1 = 10 });
// dbEntry.EquipCrestSlotType1CrestId1 = 0
MyDbEntity dbEntry = mapper.Map<MyDbEntity>(dto);
public class MyDto
{
public int equip_crest_slot_type_1_crest_id_1 { get; set; }
}
public class MyDbEntity
{
public int EquipCrestSlotType1CrestId1 { get; set; }
}
When creating the DB entity from a DTO, the property is missed out, which is odd since it works mapping one way. I wondered if the problem was that ReverseMap()
doesn't validate, and I can see that if I make an actual reverse profile:
using AutoMapper;
MapperConfiguration config =
new(cfg =>
{
cfg.SourceMemberNamingConvention = LowerUnderscoreNamingConvention.Instance;
cfg.DestinationMemberNamingConvention = PascalCaseNamingConvention.Instance;
cfg.CreateMap<MyDto, MyDbEntity>();
});
config.AssertConfigurationIsValid();
that I do get an exception
Unhandled exception. 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
====================
MyDto -> MyDbEntity (Destination member list)
MyDto -> MyDbEntity (Destination member list)
Unmapped properties:
EquipCrestSlotType1CrestId1
I'm not sure why a profile would work one way and not the other, but I assume the issue is that I've misunderstood how Pascal case is supposed to work when numbers are in the middle of a property name. The lower_underscore names are received from a client and I have no control over them, but I can rename the database entities.
Is there a way that I can resolve this without manually assigning the members? I am on AutoMapper 12.0.0 if it matters.
You can always replace the default naming convention if it doesn't work in your particular use case. The issue is that the PascalCaseNamingConvention
is tokenizing the name as ["Equip", "Crest", "Slot", "Type1", "Crest" "Id1"]
, instead of taking the numbers as separate tokens as the LowerUnderscoreNamingConvention
is in the lower-underscore names.
In this case, you can just make a class that is slightly different to the existing PascalCaseNamingConvention
, which has the following source code:
public sealed class PascalCaseNamingConvention : INamingConvention
{
public static readonly PascalCaseNamingConvention Instance = new();
public string SeparatorCharacter => "";
public string[] Split(string input)
{
List<string> result = null;
int lower = 0;
for(int index = 1; index < input.Length; index++)
{
if (char.IsUpper(input[index]))
{
result ??= new();
result.Add(input[lower..index]);
lower = index;
}
}
if (result == null)
{
return Array.Empty<string>();
}
result.Add(input[lower..]);
return result.ToArray();
}
}
Simply change the Split
function to tokenize the digits separately.