Преобразовать словарь в ICollection в Entity Framework Core - PullRequest
1 голос
/ 23 апреля 2019

Здесь я застрял с преобразованием dictionary в Icollection в EF Core.У меня есть Dictionary в FlatEmployee классе, в котором я храню список ключей, пары значений в базе данных.Я объявил так:

public class FlatEmployee
{
    public int EmployeeId { get; set; }
    public Dictionary<string, long> PayAndAllowances { get; set; }
}

//====================Configuration 
public void Configure(EntityTypeBuilder<FlatEmployee> builder)
{
        builder.HasKey(f => new { f.EmployeeId });
        builder.Property(sb => sb.PayAndAllowances)
            .HasConversion(
                pa => JsonConvert.SerializeObject(pa),
                pa => JsonConvert.DeserializeObject<Dictionary<string, long>>(pa));
}

Это работает абсолютно нормально, когда я высеваю или вставляю.Но проблема, с которой я сталкиваюсь, когда пытаюсь получить класс FlatEmployee.Это потому, что я хочу получить словарь в коллекции.Для этой цели я объявил другой класс, подобный этому:

public class LastPayCertificateViewModel: IHaveCustomMapping
{
    public int EmployeeCode { get; set; }
    public EmployeeEarningDTO PayAndAllowances { get; set; }

    public void CreateMappings(Profile configuration)
    {
        configuration.CreateMap<FlatEmployee, LastPayCertificateViewModel>()
            .ForMember(dto => dto.EmployeeCode , opt => opt.MapFrom(entity => entity.EmployeeId ));
    }
}

public class EmployeeEarningDTO : IHaveCustomMapping
{
    public ICollection<BaseEmployeeDictionary> PayAndAllowances { get; set; }

    public void CreateMappings(Profile configuration)
    {
        configuration.CreateMap<FlatEmployee, EmployeeEarningDTO>()
            .ForMember(dto => dto.PayAndAllowances, opt => opt.MapFrom(entity => entity.PayAndAllowances));
    }
}

public class BaseEmployeeDictionary
{
    public string Name { get; set; }
    public long Amount { get; set; }
}

Когда я пытаюсь использовать вышеупомянутые классы для получения данных, подобных этому:

public class LastPayCertificateQuery : IRequest<LastPayCertificateViewModel>
{
    public string EmployeeCode { get; set; }
}

public async Task<LastPayCertificateViewModel> Handle(LastPayCertificateQuery request, CancellationToken cancellationToken)
{           
    var predicate = PredicateBuilder.False<FlatEmployee>();
    predicate = predicate.Or(emp => emp.EmployeeId == request.EmployeeCode);
    var employee = await _context.FlatEmployee
                        .Where(predicate)
                        .FirstOrDefaultAsync(cancellationToken);
    if (employee == null)
        return null;
    var empVM = _mapper.Map<LastPayCertificateViewModel>(employee);
}

Тогда я получаю ноль вPayAndAllowances in empVM.Вот в чем моя проблема.Где моя проблема?Я думал, что это потому, что в словаре есть пара ключ-значение, и его невозможно преобразовать в BaseEmployeeDictionary.Я пробовал так же, чтобы добавить элемент списка в PayAndAllowances in empVM

 foreach (KeyValuePair<string, long> data in employee.DeductionsByAdjustment)
 {
     BaseEmployeeDictionary listItem = new BaseEmployeeDictionary
     {
         Name = data.Key,
         Amount = data.Value
     };
     empVM.EarningDetails.PayAndAllowances.Add(listItem);
     return empVM;
  }

, который, конечно, не будет работать, потому что empVM.EarningDetails.PayAndAllowances равен нулю и выдает исключение NullReferenceException.Мои запросы - как сопоставить между словарем и ICollection при создании карты в EmployeeEarningDTO.ИЛИ Было бы очень полезно за ваше ценное предложение и решение, пожалуйста.

1 Ответ

2 голосов
/ 23 апреля 2019

Оказывается, проблема с отображением 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 })));
...