Оказывается, проблема с отображением AutoMapper.
Сначала EmployeeEarningDTO
внутри LastPayCertificateViewModel
создает дополнительный уровень по сравнению с FlatEmployee
:
LastPayCertificateViewModel.PayPayAndAllowances.PayAndAllowances
против
FlatEmployee.PayAndAllowances
AutoMapper сопоставляет свойства по умолчанию с тем же именем. Таким образом, внутри FlatEmployee
на LastPayCertificateViewModel
map он будет пытаться отобразить Dictionary<string, long> PayAndAllowances
на EmployeeEarningDTO PayAndAllowances
. Но нет сопоставления от Dictionary<string, long>
до EmployeeEarningDTO
. Вместо этого есть отображение от FlatEmployee
до EmployeeEarningDTO
, поэтому вы должны указать AM использовать его:
configuration.CreateMap<FlatEmployee, LastPayCertificateViewModel>()
.ForMember(dto => dto.EmployeeCode, opt => opt.MapFrom(entity => entity.EmployeeId))
.ForMember(dto => dto.PayAndAllowances, opt => opt.MapFrom(entity => entity)); // <--
Во-вторых, сопоставление от FlatEmployee
до EmployeeEarningDTO
- AM автоматически попытается сопоставить свойства PayAndAllowances
, но сопоставление от KeyValuePair<string, long>
до BaseEmployeeDictionary
отсутствует. Вы можете определить такое отображение
configuration.CreateMap<KeyValuePair<string, long>, BaseEmployeeDictionary>()
.ForMember(dst => dst.Name, opt => opt.MapFrom(src => src.Key))
.ForMember(dst => dst.Amount, opt => opt.MapFrom(src => src.Value));
, который позволит вам просто использовать
configuration.CreateMap<FlatEmployee, EmployeeEarningDTO>();
однако вы, вероятно, не будете этого делать, потому что не хотите, чтобы каждые KeyValuePair<string, long>
отображались на BaseEmployeeDictionary
, поэтому вы можете сделать это отображение на месте:
configuration.CreateMap<FlatEmployee, EmployeeEarningDTO>()
.ForMember(dto => dto.PayAndAllowances, opt => opt.MapFrom(entity => entity.PayAndAllowances
.Select(src => new BaseEmployeeDictionary { Name = src.Key, Amount = src.Value })));