使用AutoMapper将嵌套元素映射到相关列表

时间:2017-09-12 22:18:36

标签: c# automapper

我有两组对象

我在C#客户端应用程序中使用的对象:

public class EmployeeClient
{
    public int Id { get; set; }

    public int DepartmentId { get; set; }

    public string FirstName { get; set; }

    public string LastName { get; set; }

    public string MiddleName { get; set; }
}

public class DepartmentClient
{
    public int Id { get; set; }

    public string Name { get; set; }
}

public class OrganizationClient
{
    public int Id { get; set; }

    public string Name { get; set; }

    public List<DepartmentClient> Departments { get; set; }

    public List<EmployeeClient> Employees { get; set; }
}

和DTO:

public class EmployeeDto
{
    public int Id { get; set; }

    public string FirstName { get; set; }

    public string LastName { get; set; }

    public string MiddleName { get; set; }
}

public class DepartmentDto
{
    public int Id { get; set; }

    public string Name { get; set; }

    public List<EmployeeDto> Employees { get; set; }
}

public class OrganizationDto
{
    public int Id { get; set; }

    public string Name { get; set; }

    public List<DepartmentDto> Departments { get; set; }
}

我使用AutoMapper,我需要配置映射客户端 - &gt; DTO和DTO - &gt;客户。 我实现了映射DTOs-&gt; Client这样:

public class DtoToClientMappingProfile: Profile
{
    public DtoToClientMappingProfile()
    {
        CreateMap<EmployeeDto, EmployeeClient>();

        CreateMap<DepartmentDto, DepartmentClient>();

        CreateMap<OrganizationDto, OrganizationClient>()
            .ForMember(dest => dest.Employees, opt => opt.ResolveUsing(src => src.Departments.SelectMany(d => d.Employees)))
            .AfterMap(AfterMap);
    }

    private void AfterMap(OrganizationDto dto, OrganizationClient client)
    {
        foreach (var department in dto.Departments)
        {
            foreach (var employee in department.Employees)
            {
                var clientEmployee = client.Employees.First(e => e.Id == employee.Id);
                clientEmployee.DepartmentId = department.Id;
            }
        }
    }
}

这不是通用的解决方案,但对我有用。

我发现只有一个选项可以实现客户端&gt; DTO的映射:

public class ClientToDtosMappingProfile : Profile
{
    public ClientToDtosMappingProfile()
    {
        CreateMap<EmployeeClient, EmployeeDto>();

        CreateMap<DepartmentClient, DepartmentDto>();

        CreateMap<OrganizationClient, OrganizationDto>()
            .AfterMap(AfterMap);
    }

    private void AfterMap(OrganizationClient client, OrganizationDto dto)
    {
        foreach (var employee in client.Employees)
        {
            var departmentDto = dto.Departments.First(d => d.Id == employee.DepartmentId);
            if (departmentDto.Employees == null)
            {
                departmentDto.Employees = new List<EmployeeDto>();
            }

            var configuration = (IConfigurationProvider)new MapperConfiguration(cfg =>
            {
                cfg.AddProfiles(typeof(ClientToDtosMappingProfile));
            });

            var mapper = (IMapper)new Mapper(configuration);

            var employeeDto = mapper.Map<EmployeeDto>(employee);
            departmentDto.Employees.Add(employeeDto);
        }
    }
}

它有效,但我不喜欢这个解决方案,因为每次映射对象时我都应该创建新Mapper的实例。在我的真实代码中,Employee有许多嵌套元素,并且映射在多个配置文件中配置。

任何想法如何更好地实施?

2 个答案:

答案 0 :(得分:1)

我使用ResolutionContext使代码更好一些。它允许不在AfterMap函数中创建映射器。

DtoToClientMappingProfile:

public class DtoToClientMappingProfile: Profile
{
    public DtoToClientMappingProfile()
    {
        CreateMap<EmployeeDto, EmployeeClient>();

        CreateMap<DepartmentDto, DepartmentClient>();

        CreateMap<OrganizationDto, OrganizationClient>()
            .ForMember(dest => dest.Employees, opt => opt.Ignore())
            .AfterMap(AfterMap);
    }

    private void AfterMap(OrganizationDto dto, OrganizationClient client, ResolutionContext resolutionContext)
    {
        if (dto.Departments == null)
        {
            return;
        }

        client.Departments = new List<DepartmentClient>();
        foreach (var department in dto.Departments)
        {
            var departmentClient = resolutionContext.Mapper.Map<DepartmentClient>(department);
            client.Departments.Add(departmentClient);
            if (department.Employees == null)
            {
                continue;
            }

            if (client.Employees == null)
            {
                client.Employees = new List<EmployeeClient>();
            }

            foreach (var employee in department.Employees)
            {
                var employeeClient = resolutionContext.Mapper.Map<EmployeeClient>(employee);
                employeeClient.DepartmentId = department.Id;
                client.Employees.Add(employeeClient);
            }
        }
    }

ClientToDtosMappingProfile:

public class ClientToDtosMappingProfile : Profile
{
    public ClientToDtosMappingProfile()
    {
        CreateMap<EmployeeClient, EmployeeDto>();

        CreateMap<DepartmentClient, DepartmentDto>();

        CreateMap<OrganizationClient, OrganizationDto>()
            .AfterMap(AfterMap);
    }

    private void AfterMap(OrganizationClient client, OrganizationDto dto, ResolutionContext resolutionContext)
    {
        if (client.Employees == null)
        {
            return;
        }

        foreach (var employee in client.Employees)
        {
            var departmentDto = dto.Departments.First(d => d.Id == employee.DepartmentId);
            if (departmentDto.Employees == null)
            {
                departmentDto.Employees = new List<EmployeeDto>();
            }

            var employeeDto = resolutionContext.Mapper.Map<EmployeeDto>(employee);
            departmentDto.Employees.Add(employeeDto);
        }
    }
}

答案 1 :(得分:0)

如果您致电AssertConfigurationIsValid,AM会抱怨它不知道如何映射。

问题似乎是您没有填充源对象中的目标对象所需的信息。

您需要为AM抱怨的每个属性添加一个解析器,例如您已经拥有的ResolveUsing

您还需要pass所需的额外信息。

结果可能最终看起来不太好,因为AM不能依靠统一的对象来完成它的工作,你必须告诉它该怎么做。

另一种方法是在你自己的代码中进行高级映射,并且仅在映射足够简单时才依赖AM,因此AM可以自己完成。您自定义AM越多,从中获得的价值就越低。