c# - 如何使用 Automapper 展平实体层次结构列表?
问题描述
我想使用 automapper 展平从 Entity Framework Core 返回的实体层次结构列表。
这是我的实体:
public class Employee {
public int Id { get; set; }
[Required]
public string Name { get; set; }
public double? PayRateRegular { get; set; }
public double? PayRateLoadedRegular { get; set; }
public double? GMOutput { get; set; }
public string EmployeeType { get; set; }
//List of CommissionDetails where this employee is the consultant
public IEnumerable<CommissionDetail> CommissionDetailConsultants { get; set; } = new List<CommissionDetail>();
}
public class Project {
public int Id { get; set; }
public string Description { get; set; }
public double? BillRateRegular { get; set; }
public DateTime? StartDate { get; set; }
public DateTime? EndDate { get; set; }
public Customer Customer { get; set; }
public int CustomerId { get; set; }
}
public class Customer {
public int Id { get; set; }
public string Name { get; set; }
}
public class CommissionDetail {
public string SaleType { get; set; }
public double CommissionPercent { get; set; }
public bool? IsReported { get; set; }
public int? Level { get; set; }
public string BasedOn { get; set; }
public Project Project { get; set; }
public int ProjectId { get; set; }
public Employee SalesPerson { get; set; }
public int SalesPersonEmployeeId { get; set; }
public Employee Consultant { get; set; }
public int ConsultantEmployeeId { get; set; }
}
这是我的 DTO:
public class ConsultantGridViewModel
{
public string ConsultantName { get; set; }
public string CustomerName { get; set; }
public string SalesPersonName { get; set; }
public string ProjectDescription { get; set; }
public double? PayRate { get; set; }
public double? LoadedRated { get; set; }
public double? BillRate { get; set; }
public double? GM { get; set; }
public DateTime? StartDate { get; set; }
public DateTime? EndDate { get; set; }
public double CommissionPercent { get; set; }
public int? CommissionLevel { get; set; }
}
这是我给 EF 的电话:
return await _dbContext.Employee
.AsNoTracking()
.Include(e => e.CommissionDetailConsultants)
.ThenInclude(cd => cd.SalesPerson)
.Include(e => e.CommissionDetailConsultants)
.ThenInclude(cd => cd.Project)
.ThenInclude(p => p.Customer)
.Where(e => e.EmployeeType == "Contractor")
.ToListAsync();
我目前正在使用 SelectMany 将其展平,如下所示:
var consultants = employees.SelectMany(e =>
e.CommissionDetailConsultants,
(emp, com) => new ConsultantGridViewModel {
ConsultantName = emp.Name,
PayRate = emp.PayRateRegular,
LoadedRated = emp.PayRateLoadedRegular,
GM = emp.GMOutput,
BillRate = com.Project.BillRateRegular,
ProjectDescription = com.Project.Description,
ProjectStartDate = com.Project.StartDate,
ProjectEndDate = com.Project.EndDate,
CustomerName = com.Project.Customer.Name,
SalesPersonName = com.SalesPerson.Name,
CommissionPercent = com.CommissionPercent,
CommissionLevel = com.Level
});
我想改用自动映射器。我已经将 automapper 用于所有其他 DTO 映射,但我不知道如何使用它来展平这样的嵌套对象。
解决方案
让我们使用导航属性用SelectMany
+重写您当前拥有的内容:Select
Consultant
var consultants = employees
.SelectMany(e => e.CommissionDetailConsultants)
.Select(com => new ConsultantGridViewModel
{
ConsultantName = com.Consultant.Name,
PayRate = com.Consultant.PayRateRegular,
LoadedRated = com.Consultant.PayRateLoadedRegular,
GM = com.Consultant.GMOutput,
BillRate = com.Project.BillRateRegular,
ProjectDescription = com.Project.Description,
ProjectStartDate = com.Project.StartDate,
ProjectEndDate = com.Project.EndDate,
CustomerName = com.Project.Customer.Name,
SalesPersonName = com.SalesPerson.Name,
CommissionPercent = com.CommissionPercent,
CommissionLevel = com.Level
});
现在可以看到CommissionDetail
包含所有必要的数据,所以虽然你无法避免,但你可以通过创建一个映射 from to并使用类似这样的东西SelectMany
来替换:Select
CommissionDetail
ConsultantGridViewModel
var consultants = Mapper.Map<List<ConsultantGridViewModel>>(
employees.SelectMany(e => e.CommissionDetailConsultants));
甚至更好的是,直接投射到 DTO:
var consultants = await _dbContext.Employee
.Where(e => e.EmployeeType == "Contractor")
.SelectMany(e => e.CommissionDetailConsultants)
.ProjectTo<ConsultantGridViewModel>()
.ToListAsync();
现在映射。
AutoMapper 将自动映射成员,例如CommisionPercent
. 此外,展平功能将自动处理Project.EndDate
-> ProjectEndDate
、Consultant.Name
->ConsultantName
等 映射。
因此,与 AutoMapper 一样,您应该手动指定不属于先前类别的属性的映射。在这种情况下,最小配置是这样的:
Mapper.Initialize(cfg =>
{
cfg.CreateMap<CommissionDetail, ConsultantGridViewModel>()
.ForMember(dst => dst.PayRate, opt => opt.MapFrom(src => src.Consultant.PayRateRegular))
.ForMember(dst => dst.LoadedRated, opt => opt.MapFrom(src => src.Consultant.PayRateLoadedRegular))
.ForMember(dst => dst.GM, opt => opt.MapFrom(src => src.Consultant.GMOutput))
.ForMember(dst => dst.BillRate, opt => opt.MapFrom(src => src.Project.BillRateRegular))
.ForMember(dst => dst.CustomerName, opt => opt.MapFrom(src => src.Project.Customer.Name))
.ForMember(dst => dst.CommissionLevel, opt => opt.MapFrom(src => src.Level));
});
PS您甚至可以SelectMany
通过直接基于CommissionDetail
实体的查询来避免,例如
var consultants = await _dbContext.Set<CommissionDetail>()
.Where(c => c.Consultant.EmployeeType == "Contractor")
.ProjectTo<ConsultantGridViewModel>()
.ToListAsync();
请注意,当您进行直接投影时,不需要AsNoTracking
or Include
/ ThenInclude
。
推荐阅读
- inheritance - 如何在odoo12中的“account.invoice”表单视图中显示模型“sale.order”的字段
- c# - C# Selenium:如何在 Sharepoint 文件夹上传对话框中单击不在 DOM 中的接受按钮
- spring - Spring Boot Upload Multipart 413 请求实体太大
- javascript - 如何从循环范围中计数
- angular - 如何使用 *ngFor 仅显示数组中的一项?
- c# - UnityEvent 未在检查器中显示动态方法调用(其他统一事件也部分中断)
- javascript - 为什么我的分数第一次没有更新?(剪刀石头布)
- ansible - 使用 gitlab,如何触发 CI 使用不同的 YAML 文件进行 ansible?
- android - 无法使用 intellij 或 android studio 构建空的颤振项目
- python-3.x - 有没有办法将多个数据框导出到一个 excel 文件中?