如何使用 Automapper 展平实体层次结构列表?

Ale*_*lex 1 c# entity-framework automapper entity-framework-core

我想使用自动映射器来展平从 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; }

}
Run Code Online (Sandbox Code Playgroud)

这是我的 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; }

}
Run Code Online (Sandbox Code Playgroud)

这是我给 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();
Run Code Online (Sandbox Code Playgroud)

我目前正在使用 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
    });    
Run Code Online (Sandbox Code Playgroud)

我想改用自动映射器。我已将 automapper 用于所有其他 DTO 映射,但我不知道如何使用它来展平像这样的嵌套对象。

Iva*_*oev 5

让我们使用导航属性重写当前SelectManySelect内容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
    });
Run Code Online (Sandbox Code Playgroud)

现在可以看到 包含CommissionDetail所有必要的数据,因此虽然您无法避免,但您可以通过创建从到 的映射SelectMany来替换,并使用如下所示的内容:SelectCommissionDetailConsultantGridViewModel

var consultants = Mapper.Map<List<ConsultantGridViewModel>>(
    employees.SelectMany(e => e.CommissionDetailConsultants));
Run Code Online (Sandbox Code Playgroud)

或者更好的是,直接投影到 DTO:

var consultants = await _dbContext.Employee
    .Where(e => e.EmployeeType == "Contractor")
    .SelectMany(e => e.CommissionDetailConsultants)
    .ProjectTo<ConsultantGridViewModel>()
    .ToListAsync();
Run Code Online (Sandbox Code Playgroud)

现在是映射。

AutoMapper 将自动映射成员,例如CommisionPercent. 此外,扁平化功能将自动处理映射,例如Project.EndDate-> ProjectEndDateConsultant.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));
});
Run Code Online (Sandbox Code Playgroud)

PS 您甚至可以SelectMany通过直接基于实体进行查询来避免CommissionDetail,例如

var consultants = await _dbContext.Set<CommissionDetail>()
    .Where(c => c.Consultant.EmployeeType == "Contractor")
    .ProjectTo<ConsultantGridViewModel>()
    .ToListAsync();
Run Code Online (Sandbox Code Playgroud)

请注意,当您进行直接投影时,不需要AsNoTrackingInclude/ ThenInclude