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());