ASP .NET Core -  Usando AutoMapper (revisitado) - I


 Hoje vamos voltar a usar o AutoMapper em uma aplicação Web API ASP .NET Core.

O AutoMapper é uma biblioteca pequena e simples que permite realizar o mapeamento de um objeto para outro objeto e que pode ser usada para mapear objetos pertencentes a tipos diferentes. O mapeamento objeto-objeto funciona transformando um objeto de entrada de um tipo em um objeto de saída de um tipo diferente.

Quando e porque usar o AutoMapper

A utilização do AutoMapper vem a calhar quando você precisa realizar o mapeamento entre objetos e isso pode ocorrer em diversos cenários:

- Resolver o conflito entre as responsabilidades das camadas de uma aplicação através do mapeamento entre os objetos
- Segregar os modelos usados nas camadas de forma a resolver este conflito fazendo o mapeamento objeto-objeto
- Isolar o modelo usado em uma camada de forma a expor somente algumas das suas propriedades através do mapeamento entre os objetos

Esses cenários são muitos comuns em aplicações ASP .NET Core MVC e aplicações Web API com mais de uma camada, mas a utilização do AutoMapper não esta restrita a somente esses tipos de projetos.

A seguir veremos como configurar o AutoMapper em aplicações na plataforma .NET

Configurando o AutoMapper

Para usar o AutoMapper em aplicações Console basta instalar o pacote AutoMaper:

A seguir criamos um mapeador para os tipos a serem mapeados usando o construtor da classe MapperConfiguration:

var config = new MapperConfiguration(cfg =>  cfg.CreateMap< Origem , Destino >() );

var mapper = new Mapper(config);

var objeto = mapper.Map<destino>(origem);

Depois criamos uma instância do mapeador criado com a configuração definida e aplicamos o mapeamento entre os objetos de destino e origem.

Nas aplicações ASP .NET Core MVC e Web API  basta instalar o pacote AutoMapper.Extensions.Microsoft.DependencyInjection

Com isso o pacote do AutoMapper será instalado como uma dependência.

A seguir no método ConfigureServices da classe Startup usamos o método de extensão AddAUtoMapper para ativar o serviço do AutoMapper :


services.AddAutoMapper(AppDomain.CurrentDomain.GetAssemblies());
 

E com isso podemos injetar a interface IMapper nos controladores e serviço para usar os recursos do AutoMapper.

Realizando o mapeamento com AutoMapper

Realizar o mapeamento é a tarefa mais importante após configurar o AutoMapper, e a melhor forma de fazer isso é criar um ou mais perfis de mapeamento ou Mapping Profile.

Os perfis de mapeamento que permitem agrupar configurações de mapeamento. Para isso criamos uma classe onde para cada entidade podemos definir uma classe de mapeamento que herda de Profile.

Abaixo temos um exemplo de uma classe MappingProfile definindo um perfil de mapeamento básico:

public class MappingProfile : Profile
{
     public MappingProfile()
     {
        CreateMap<Origem, Destino>();
      }
}

Quando os nomes das propriedades do tipo de destino forem iguais as do tipo de origem o AutoMapper usa as convenções  e o mapeamento básico mostrado neste código é tudo que você precisa fazer.

Quando os nomes dos membros dos tipos de origem e destino forem diferentes usamos o método ForMember informando o membro de destino e o método MapFrom para indicar a origem.

No exemplo a seguir o tipo de destino FuncionarioDTO possui uma propriedade NomeCompleto que devemos mapear a partir do tipo de origem Funcionario usando as propriedades Nome e Sobrenome:

public class MappingProfile : Profile
{
      public MappingProfile()
      {
            CreateMap<Funcionario, FuncionarioDTO>()
               .ForMember(dest => dest.NomeCompleto,
                    map => map.MapFrom(src => $"{src.Nome} {src.Sobrenome}")).ReverseMap();
       }
}

Além disso o AutoMapper suporta o mapeamento bidirecional através da clausula  . ReverseMap();

A seguir veremos um exemplo usando uma aplicação ASP .NET Core Web API criada no VS 2019.

Criando o projeto Web API no VS 2019

Abra o VS 2019 e clique em New Project e selecione o template ASP .NET Core Web API e clique em Next;

Informe o nome Api_AutoMapper e clique em Next;

A seguir selecione o Target Framework, Authentication Type e demais configurações conforme mostrada na figura:

Clique em Create.

Vamos incluir no projeto o pacote do AutoMapper.Extensions.Microsoft.DependencyInjection via menu Tools usando a opção Manage Nuget Packages for Solution;

Selecione a guia Browse e informe o nome do pacote , selecione o projeto e clique em Install:

Após isso vamos configurar o serviço do AutoMapper na classe Startup:

public void ConfigureServices(IServiceCollection services)
 {
            services.AddAutoMapper(AppDomain.CurrentDomain.GetAssemblies());

            services.AddControllers();
            services.AddSwaggerGen(c =>
            {
                c.SwaggerDoc("v1", new OpenApiInfo { Title = "Api_AutoMapper", Version = "v1" });
            });
}

A seguir vamos criar no projeto a pasta Models e definir o modelo de domínio com 2 classes e uma enumeração:

1- Funcionario

public class Funcionario
 {
        public string Nome { get; set; }
        public string Sobrenome { get; set; }
        public Sexo Sexo { get; set; }
        public Endereco Endereco { get; set; }
        public decimal Salario { get; set; }
}

2- Endereco

public class Endereco
{
      public string Cidade { get; set; }
}

3- Sexo

public enum Sexo
{
    Feminino,
    Masculino
}

Definimos assim o nosso modelo de domínio e que representam os tipos de origem no AutoMapper.

A seguir vamos definir os tipos de destino criando no projeto a pasta DTOs e as classes FuncionarioDTO e EnderecoDTO.

1- FuncionatioDTO

public class FuncionarioDTO
 {
         public string NomeCompleto { get; set; }
         public string Sexo { get; set; }
         public EnderecoDTO Endereco { get; set; }
         public bool IsAtivo { get; set; }
}

2- EnderecoDTO

public class EnderecoDTO
{
      public string Cidade { get; set; }
}

Nosso objetivo será mapear  as entidades do nosso modelo de domínio para os tipos FuncionarioDTO e EnderecoDTO. Observe que os membros definidos em FuncionarioDTO são diferentes dos membros definidos em Funcionario e teremos que realizar o mapeamento entre os objetos usando os métodos ForMember e FromMap definidos em uma classe de perfil de gerenciamento.

Vamos então criar a pasta Mappings no projeto e criar a classe MappingProfile com o código abaixo:

using Api_AutoMapper.DTOs;
using Api_AutoMapper.Models;
using AutoMapper;

namespace Api_AutoMapper.Mappings
{
    public class MappingProfile : Profile
    {
        public MappingProfile()
        {
            CreateMap<Funcionario, FuncionarioDTO>()
                .ForMember(dest => dest.Sexo, map =>
                    map.MapFrom(src => src.Sexo == Sexo.Masculino ? "M" : "F"))
                .ForMember(dest => dest.NomeCompleto,
                    map => map.MapFrom(src => $"{src.Nome} {src.Sobrenome}"))
                .ForMember(dest => dest.IsAtivo, src =>
                    src.MapFrom(src => src.Salario > 0 ? true : false))
                .ReverseMap();

            CreateMap<Endereco, EnderecoDTO>();
        }
    }
}

Definimos na classe MappingProfile os seguintes mapeamentos:

1- Criamos o mapeamento para Funcionaro/FuncionarioDTO definindo o tipo de origem e o tipo de destino:

      CreateMap<Funcionario, FuncionarioDTO>()

2- A seguir estamos mapeando a enumeração Sexo da origem para atribuir o valor 'M' no destino quando o valor 'Masculino' e F quando for 'Feminino';

     .ForMember(dest => dest.Sexo, map => map.MapFrom(src => src.Sexo == Sexo.Masculino ? "M" : "F"))

3- Mapeamos a concatenação de Nome e Sobrenome na origem para NomeCompleto no destino:

   .ForMember(dest => dest.NomeCompleto, map => map.MapFrom(src => $"{src.Nome} {src.Sobrenome}"))

4-  Mapeamos o valor true para o membro IsAtivo no destino quando o valor de Salario for maior que zero e false quando for igual ou menor que zero:

   .ForMember(dest => dest.IsAtivo, src =>  src.MapFrom(src => src.Salario > 0 ? true : false))

5- Criamos o mapeamento para os tipos Endereco/EnderecoDTO

      CreateMap<Endereco, EnderecoDTO>();

Assim estaremos exibindo ao usuário os valores definidos no tipo FuncionarioDTO e assim ocultando o nosso modelo de domínio da camada de apresentação.

Na próxima parte do artigo vamos criar um controlador na pasta Controllers para poder testar o mapeamento feito.

"Bem-aventurados os pobres de espírito, porque deles é o reino dos céus; Bem-aventurados os que choram, porque eles serão consolados; "
Mateus 5:3,4

Referências:


José Carlos Macoratti