Qui sono bloccato con la conversione del dictionary
in Icollection
in EF Core. Ho un Dictionary
nella classe FlatEmployee
in cui sto memorizzando un elenco di chiavi, coppia di valori nel database. Ho dichiarato così:
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));
}
Funziona perfettamente quando eseguo il seeding o l'inserimento. Ma il problema che sto affrontando quando sto cercando di ottenere la classe FlatEmployee. Questo perché voglio ottenere il dizionario in Collection. A tale scopo ho dichiarato un'altra classe come questa:
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; }
}
Quando sto cercando di utilizzare le classi precedenti per ottenere i dati in questo modo:
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);
}
Quindi sto diventando null in PayAndAllowances in empVM
. Questo è il mio problema. Dov'è il mio problema? Ho pensato che fosse perché quel dizionario ha una coppia chiave-valore e che non è stato in grado di convertire in BaseEmployeeDictionary
. Ho provato anche in questo modo per aggiungere la voce di elenco a 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;
}
Il che ovviamente non funzionerà perché empVM.EarningDetails.PayAndAllowances
è null e genera NullReferenceException. Le mie domande sono come mappare tra il dizionario e ICollection durante la creazione della mappa in EmployeeEarningDTO
. O Sarebbe davvero apprezzato per il tuo prezioso suggerimento e soluzione per favore.
Risulta essere un problema di mappatura di AutoMapper.
Innanzitutto, EmployeeEarningDTO
all'interno di LastPayCertificateViewModel
crea un livello aggiuntivo rispetto a FlatEmployee
:
LastPayCertificateViewModel.PayPayAndAllowances.PayAndAllowances
vs
FlatEmployee.PayAndAllowances
AutoMapper esegue il mapping delle proprietà predefinite con lo stesso nome. Quindi, all'interno di FlatEmployee
su LastPayCertificateViewModel
map, si cercherebbe di mappare Dictionary<string, long> PayAndAllowances
a EmployeeEarningDTO PayAndAllowances
. Ma non esiste alcun mapping da Dictionary<string, long>
a EmployeeEarningDTO
. Invece, c'è un mapping da FlatEmployee
a EmployeeEarningDTO
, quindi devi dire ad AM di usarlo:
configuration.CreateMap<FlatEmployee, LastPayCertificateViewModel>()
.ForMember(dto => dto.EmployeeCode, opt => opt.MapFrom(entity => entity.EmployeeId))
.ForMember(dto => dto.PayAndAllowances, opt => opt.MapFrom(entity => entity)); // <--
In secondo luogo, il mapping da FlatEmployee
a EmployeeEarningDTO
- AM proverà automaticamente a mappare PayAndAllowances
proprietà PayAndAllowances
, ma non esiste alcun mapping da KeyValuePair<string, long>
a BaseEmployeeDictionary
. È possibile definire tale mappatura
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));
che ti permetterà di usare semplicemente
configuration.CreateMap<FlatEmployee, EmployeeEarningDTO>();
tuttavia probabilmente non lo farai perché non vuoi che ogni KeyValuePair<string, long>
sia mappato su BaseEmployeeDictionary
, quindi puoi fare quel mapping sul posto:
configuration.CreateMap<FlatEmployee, EmployeeEarningDTO>()
.ForMember(dto => dto.PayAndAllowances, opt => opt.MapFrom(entity => entity.PayAndAllowances
.Select(src => new BaseEmployeeDictionary { Name = src.Key, Amount = src.Value })));