【问题标题】:Convert Dictionary to ICollection in Entity Framework Core在 Entity Framework Core 中将字典转换为 ICollection
【发布时间】:2023-03-16 19:40:02
【问题描述】:

在这里,我被 EF Core 中的 dictionary 转换为 Icollection 所困扰。我在FlatEmployee 类中有Dictionary,我在其中存储了数据库中的键、值对列表。我是这样声明的:

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 类时遇到的问题。这是因为我想在 Collection 中获取字典。为此,我声明了另一个这样的类:

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 中得到空值。这就是我的问题所在。我的问题在哪里?我认为这是因为 Dictionary 具有键值对并且无法转换为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 为 null 并引发 NullReferenceException。我的疑问是在EmployeeEarningDTO 中创建地图时如何在字典和 ICollection 之间进行映射。或者非常感谢您提出宝贵的建议和解决方案。

【问题讨论】:

  • 我认为 EF Core 不支持这种类型的映射。 FlatEmployee.PayAndAllowances应该如何存储在对应的数据库表中?
  • 它正在存储这样的数据{"Basic Pay":10000,"DA":20000,"HRA":10000}
  • JSON?在文本列中?看看它是如何工作的(例如插入数据)很有趣。你在用ValueConverter吗?请显示 FlatEmployee 实体的流畅配置 - 如果 EF Core 无法读取从数据库填充 PayAndAllowancesFlatEmployee 对象,则您发布的所有其他代码都无关紧要。
  • 我已经用FlatEmployee 配置更新了问题。
  • 谢谢。 EF Core 读取它没有问题,因此结果与我之前的评论相反 :) - AutoMapper 映射问题。

标签: c# ef-code-first entity-framework-core asp.net-core-webapi asp.net-core-2.1


【解决方案1】:

原来是 AutoMapper 映射问题。

首先,EmployeeEarningDTOLastPayCertificateViewModel 相比 FlatEmployee 创建了额外的级别:

LastPayCertificateViewModel.PayPayAndAllowances.PayAndAllowances

FlatEmployee.PayAndAllowances

AutoMapper 默认映射同名的属性。所以在FlatEmployeeLastPayCertificateViewModel 的映射中,它会尝试将Dictionary&lt;string, long&gt; PayAndAllowances 映射到EmployeeEarningDTO PayAndAllowances。但是没有从Dictionary&lt;string, long&gt;EmployeeEarningDTO 的映射。相反,有一个从FlatEmployeeEmployeeEarningDTO 的映射,所以你必须告诉 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&lt;string, long&gt;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&lt;string, long&gt; 都映射到 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 })));

【讨论】:

  • 问题出在自动映射器上。非常感谢它解决了我的问题。
猜你喜欢
  • 2020-04-28
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-07-18
  • 2021-05-08
  • 2023-01-27
  • 1970-01-01
  • 2021-10-14
相关资源
最近更新 更多