Повторное использование кода в сопоставлениях AutoMapper ForMember - PullRequest
0 голосов
/ 26 января 2020

Я работаю над решением с Entity Framework Core и AutoMapper (версия 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. Я также попытался переписать метод в выражение Fun c, которое возвращает отдел, но затем я не могу использовать результат напрямую для доступа к свойствам отдела в отображении.

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;
    }
}

Любые предложения

1 Ответ

0 голосов
/ 26 января 2020

Это половина решения для вас, но я все равно выложу. Вы можете выполнить сопоставление, создав метод, который предоставляет выражение. Проблема в том, как вызвать исключение, если не найден подходящий отдел, поскольку вы не можете выбросить его в выражение. Я бы посоветовал создать исключение после сопоставления, поскольку вам все равно придется запрашивать данные.

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!");
    }
});
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...