Implementando Mapeamento de Objetos com AutoMapper no ABP Framework

No desenvolvimento de aplicações modernas, a conversão entre diferentes modelos de dados é uma tarefa constante. No contexto do ABP Framework, frequentemente precisamos transformar Entidades de banco de dados em Objetos de Transferência de Dados (DTOs) para comunicação com o frontend. Realizar esse mapeamento manualmente resulta em código repetitivo e propenso a erros. O AutoMapper surge como uma solução robusta para automatizar esse processo.

Injeção do IObjectMapper

Para utilizar os recursos de mapeamento no ABP, a forma recomendada é através da interface IObjectMapper. Se você estiver desenvolvendo um serviço que herda de ApplicationService, a propriedade ObjectMapper já está disponível nativamente, pois é injetada pela classe base AbpServiceBase.

Configuração de Mapeamentos no Módulo

A configuração inicial geralmente ocorre no método Initialize do módulo da sua aplicação (classe que herda de AbpModule). É aqui que definimos como as classes se relacionam.

public override void Initialize() {
    IocManager.RegisterAssemblyByConvention(typeof(MeuSistemaApplicationModule).GetAssembly());

    Configuration.Modules.AbpAutoMapper().Configurators.Add(cfg => {
        // Habilita a criação de mapas automáticos para tipos simples
        cfg.CreateMissingTypeMaps = true;

        // Exemplo de mapeamento customizado entre DTO e Entidade
        cfg.CreateMap<UsuarioDto, UsuarioEntidade>(MemberList.Source)
            .ForMember(dest => dest.NomeCompleto, opt => opt.MapFrom(src => src.PrimeiroNome))
            .ForMember(dest => dest.DataRegistro, opt => opt.Ignore());
    });
}

No exemplo acima, utilizamos o método ForMember para tratar divergências entre os nomes das propriedades ou para ignorar campos específicos que não devem ser alterados pelo mapeamento.

Organização com Profiles

À medida que o sistema cresce, declarar todos os maepamentos dentro da classe de Módulo torna o código difícil de manter. A melhor prática é utilizar classes que herdam de Profile, permitindo segmentar as regras por contexto.

public class PerfilGerenciamentoProdutos : Profile {
    public PerfilGerenciamentoProdutos() {
        CreateMap<Produto, ProdutoVisualizacaoDto>(MemberList.Destination)
            .ForMember(d => d.CategoriaNome, o => o.MapFrom(s => s.Categoria.Descricao));

        CreateMap<CriarProdutoInput, Produto>(MemberList.Source)
            .ForMember(d => d.Id, o => o.Ignore());
            
        CreateMap<AtualizarProdutoInput, Produto>(MemberList.Source)
            .ForMember(d => d.CodigoSku, o => o.Ignore());
    }
}

Para que o ABP reconheça esses perfis automaticamente, basta configurar o AutoMapper para escanear o assembly no módulo:

public override void Initialize() {
    Configuration.Modules.AbpAutoMapper().Configurators.Add(cfg => {
        cfg.AddMaps(typeof(MeuSistemaApplicationModule).GetAssembly());
    });
}

Uso de Atributos: AutoMapTo e AutoMapFrom

Para mapeamentos diretos e simples, onde os nomes das propriedades coincidem perfeitamente, o ABP oferece atributos que simplificam ainda mais o trabalho, dispensando a configuração manual no Profile ou Módulo.

[AutoMapTo(typeof(Fornecedor))]
public class CriarFornecedorDto {
    public string RazaoSocial { get; set; }
    public string Cnpj { get; set; }
}

Projeções com ProjectTo

Quando trabalhamos com grandes volumes de dados no banco de dados, mapear uma lista inteira de entidades para DTOs na memória pode ser ineficiente. O AutoMapper fornece a extensão ProjectTo para IQueryable, que permite ao Entity Framework traduzir o mapeamento diretamente para a consulta SQL.

public async Task<List<ItemEstoqueDto>> GetEstoqueResumidoAsync() {
    var consulta = _estoqueRepositorio.GetAll()
        .Where(x => x.Quantidade > 0);

    // O ProjectTo otimiza a query SQL para selecionar apenas as colunas necessárias
    return await consulta.ProjectTo<ItemEstoqueDto>(_mapper.ConfigurationProvider)
        .ToListAsync();
}

O uso do ProjectTo é altamente recomendado em operações de leitura (queries), pois melhora significativamente a performance ao evitar o carregamento de colunas desnecessárias do banco de dados.

Tratamento de Propriedades Ignoradas

É comum querer proteger campos sensíveis ou chaves primárias durante o mapeamento de entrada. Ao ignorar o campo Id, garantimos que o identificador da entidade não seja sobrescrito acidentalmente por um valor vindo do DTO.

cfg.CreateMap<EdicaoCadastroDto, Pessoa>(MemberList.Source)
    .ForMember(dest => dest.Id, opt => opt.Ignore());

Tags: ABP Framework AutoMapper .NET C# DTO

Publicado em 6-25 18:53