Bom, para quem não sabe o que é o AutoMapper, segue uma definição retirada do próprio site do projeto:
“AutoMapper is a simple little library built to solve a deceptively complex problem – getting rid of code that mapped one object to another.”
Ou seja, é um biblioteca criada para mapeamento entre objetos.
Ao longo do artigo irei demonstrar exemplos de mapeamento entre objetos simples e complexos, além de demonstrar como configurar o mapeamento e exibir os prós e contras (no meu ponto de vista) quanto ao recurso/biblioteca.
Para o exemplo estarei utilizando Linq To SQL com um projeto em ClassLibrary.
Vamos considerar as seguintes informações:
Como é possível observar, temos 3 tabelas (tbTipo, tbGrupo e tbProduto)
Nosso DBML fica da seguinte forma:
Irei criar duas classes DTO para o mapeamento entre nosso modelo, Grupo_DTO e Produto_DTO, veja:
public class Grupo_DTO
{
public string nome { get; set; }
}
public class Produto_DTO
{
public string nome { get; set; }
public decimal valor { get; set; }
public DateTime data { get; set; }
public string nomeTipo { get; set; }
public string nomeGrupo { get; set; }
}
Mapeando objetos simples
Após efetuar o download da biblioteca, devemos utilizar o namespace AutoMapper.
O mapeamento com o AutoMapper é bem simples, basicamente, é necessário que seja definido como o mapeamento será efetuado, em seguida já é possível realizar efetivamente o mapeamento. Veja um exemplo simples, utilizando as classes Grupo e Grupo_DTO:
//Define como o mapeamento será efetuado Mapper.CreateMap<Grupo, Grupo_DTO>(); //Realiza o mapeamento Grupo g = _db.Grupos.FirstOrDefault(); Grupo_DTO grupoMapeado = Mapper.Map<Grupo, Grupo_DTO>(g);
Desta forma o mapeamento já é efetuado, porém, podemos melhorar algumas coisas
. Vamos criar um método genérico, que realiza o mapeamento para qualquer objeto origem/destino:
public static IList<TDestino> RealizaMapeamento<TOrigem, TDestino>()
where TOrigem : class
where TDestino : class
{
return Mapper.Map<IQueryable<TOrigem>, IList<TDestino>>(_db.GetTable<TOrigem>());
}
public static TDestino RealizaMapeamento<TOrigem, TDestino>(TOrigem objOrigem)
where TOrigem : class
where TDestino : class
{
return Mapper.Map<TOrigem, TDestino>(objOrigem);
}
Agora podemos utilizar algo como:
//Exemplo 1 Grupo g = _db.Grupos.FirstOrDefault(); Grupo_DTO grupoMapeado = RealizaMapeamento<Grupo, Grupo_DTO>(g); //Exemplo 2 IList<Grupo_DTO> listaMapeada1 = Mapper.Map<IQueryable<Grupo>, IList<Grupo_DTO>>(_db.Grupos); //Exemplo 3 IList<Grupo_DTO> listaMapeada2 = RealizaMapeamento<Grupo, Grupo_DTO>();
Mapeando objetos complexos
Analisando a classe Produto_DTO, podemos observar que algumas propriedades são diferentes quanto a classe Produto, como nomeTipo, nomeGrupo e data. Para que estas propriedades sejam preenchidas ao efetuar o mapeamento, devemos configura-las, observe:
Mapper.CreateMap<Produto, Produto_DTO>()
.ForMember(dest => dest.data, ori => ori.MapFrom(src => src.dataCadastro))
.ForMember(dest => dest.nomeGrupo, ori => ori.MapFrom(src => src.Grupo.nome))
.ForMember(dest => dest.nomeTipo, ori => ori.MapFrom(src => (src.Tipo != null) ? src.Tipo.nome : string.Empty));
Veja que:
- O primeiro ForMember define que a propriedade dataCadastro vai preencher a propriedade data;
- O segundo ForMember define que a propriedade nome presente no objeto Grupo vai preencher a propriedade nomeGrupo;
- O ultimo ForMember define que a propriedade nome presente no objeto Tipo vai preencher a propriedade nomeTipo, porém, este verifica antes se o objeto Tipo possui valor.
Agora podemos realizar o mapeamento como anteriormente:
IList<Produto_DTO> listaMapeada3 = RealizaMapeamento<Produto, Produto_DTO>();
Observe que o mapeamento é efetuado com sucesso:
Porém…
Vá com calma, o AutoMapper não é perfeito
Isso mesmo, vou demonstrar o porque abrindo o SQL Profiler. Primeiramente vou executar uma query que realiza o mapeamento, porém apenas com Linq, veja:
IList<Produto_DTO> lista = (from p in _db.Produtos
select new Produto_DTO()
{
nome = p.nome,
data = p.dataCadastro.Value,
valor = p.valor.Value,
nomeGrupo = p.Grupo.nome,
nomeTipo = (p.Tipo != null ? p.Tipo.nome : string.Empty)
}).ToList();
Ao executarmos esta query o profiler do SQL server exibe o que realmente foi executado no BD:
Veja que foi executada apenas uma query. Ótimo.
Agora vamos limpar o profiler e executar apenas a rotina que efetua o mapeamento com o AutoMapper, observe agora o resultado:
IList<Produto_DTO> listaMapeada3 = RealizaMapeamento<Produto, Produto_DTO>();
Veja o resutado, foram executadas diversas queries, na verdade o AutoMapper efetua uma query para cada join de seu mapeamento, no caso, com as tabelas tbTipo e tbGrupo. O criador do AutoMapper (Jimmy Bogard) está ciente do problema, e o mesmo até criou um post em blog comentando, explicando e demonstrando um solução paleativa quanto ao problema, (Solução essa que seria basicamente a criação de um método de extensão para IQueryable, onde seja permitido especificar exatamente qual será o retorno quando a consulta é executada)
Outro ótimo artigo sobre este erro pode ser visto aqui.
Bom, por hoje era isso e use o AutoMapper com moderação.











Muito boa postagem, especialmente falando do efeito colateral, a maioria das pessoas esquecem de relatar que nem tudo são flores.
Parabéns.
Pingback URL