首页 > 解决方案 > 在 AutoMapper ForMember 映射中重用代码

问题描述

我正在使用Entity Framework CoreAutoMapper(版本 9)开发解决方案。从我的实体类到 DTO 的映射是通过投影完成的。

var dto = await context.Companies
    .ProjectTo<MyDto>(config, new { departmentName = "Sales" });

在映射中,我选择了某个部门。

public class MyProfile : Profile
{
    public MyProfile()
    {
        string departmentName = null;

        CreateMap<Company, MyDto>()
            .ForMember(m => m.DepartmentLocation, opt => opt.MapFrom(src =>
                src.Divisions.SelectMany(dv => 
                    dv.Departments.Where(d => d.Name == departmentName)
                )
                .Single().Location));
    }
}

是否可以将代码移动到配置文件类中的另一个(私有)方法来选择部门?


在映射 DTO 的其他成员时也需要此代码。

我试图将代码移动到另一种方法,但是没有填充该部门对公司的集合。可能是因为 EF 无法将该方法转换为 SQL。我还尝试将方法重写为返回部门的 Func 表达式,但是我不能直接使用结果来访问映射中的部门属性。

public class MyProfile : Profile
{
  public MyProfile()
    {
        string departmentName = null;

        CreateMap<Company, MyDto>()
            .ForMember(m => m.DepartmentLocation, opt => opt.MapFrom(src =>
                SelectDepartment(src, departmentName).Location)
            );
    }

    private Department SelectDepartment(Company company, string departmentName)
    {
        var department = company.Divisions
            .SelectMany(Departments.Where(d => d.Name == departmentName)
            .First();

        if (department == null)
            throw new AutoMapperMappingException($"Department '{departmentName}' not found!");

        return department;
    }
}

有什么建议么?

标签: c#entity-framework-coreautomapper

解决方案


这对你来说是一个解决方案的一半,但无论如何我都会发布它。您可以通过创建提供表达式的方法来完成映射。问题是 - 如果找不到合适的部门,如何抛出异常,因为你不能在表达式中抛出异常。我建议在映射之后抛出异常,因为无论如何您都必须查询数据。

public class MyMappingProfile : Profile
{
    public MyMappingProfile()
    {
        string departmentName = "Foo";

        CreateMap<Company, MyDto>()
            .ForMember(m => m.DepartmentLocation, opt => opt.MapFrom(SelectDepartment(departmentName)));
    }

    private Expression<Func<Company, string>> SelectDepartment(string departmentName)
        => (company) => company.Divisions
            .SelectMany(division => division.Departments)
            .Where(department => department.Name == departmentName)
            .FirstOrDefault()
            .Location;
}

EF Core 将生成很好的查询:

SELECT(
    SELECT TOP(1) [d0].[Name]
    FROM [Division] AS[d]
          INNER JOIN [Department] AS [d0] ON [d].[Id] = [d0].[DivisionId]
    WHERE ([c].[Id] = [d].[CompanyId]) 
        AND ([d0].[Name] = @__departmentName_0)) 
    AS [DepartmentLocation]
FROM [Company] AS[c]

用法:

var result = dbContext
    .Set<Company>()
    .ProjectTo<MyDto>(mapperConfiguration)
    .ToList();

result.ForEach(myDto =>
{
    if (myDto.DepartmentLocation == null)
    {
        throw new AutoMapperMappingException("Department was not found!");
    }
});

推荐阅读