Mapeamentos condicionais usando o AutoMapper

Eu me deparei com um problema diferente usando o AutoMapper. Eu precisava mapear um atributo do tipo DateTime de um web service para uma classe de negócio.

Se você já fez isso, provavelmente sabe que quando tem um atributo do tipo DateTime, você terá na sua classe de proxy dois atributos. Um do tipo DateTime e um booleano que informa se o seu DateTime está preenchido ou não.

Assim, supondo que no seu web service exista um atributo do tipo DateTime chamado CriadoEm, então você terá em sua classe de proxy dois atributos assim:

public DateTime CriadoEm { get; set; }
public bool CriadoEmSpecified { get; set; }

Bom, nesse caso, eu queria que o AutoMapper mapeasse esse atributo apenas se CriadoEmSpecified fosse = true.

Eu resolvi isso dessa forma:

Mapper.CreateMap()
  .ForMember(c => c.CriadoEm, opt => {
    opt.Condition(x => x.CriadoEmSpecified);
    opt.MapFrom(x => x.CriadoEm);
  });

É claro que isso vale para qualquer condição que você queira, então, agora é só aproveitar. Alegre

Mapeando suas classes com o AutoMapper

Um framework que eu tenho usado com frequência ultimamente é o AutoMapper, e pra quem não conhece ele, bom, ele faz.. mapeamento de objetos. Iupi!

Existem vários cenários onde isso é feito, no meu caso, principalmente, eu tenho usado para mapear estruturas de web services que eu consumo para objetos dentro da minha aplicação. Mas outro cenário comum é, quando usamos o padrão ViewModel, mapear objetos de negócio para a ViewModel.

A motivação para o uso do AutoMapper é manter as responsabilidades separadas, assim, temos um framework só para fazer isso e não precisamos colocar esse mapeamento no meio das nossas classes.

O uso do AutoMapper é bem simples, apenas um assembly que deve ser referenciado no projeto, depois disso devemos criar/definir os mapeamentos.

Vamos imaginar um cenário onde eu tenho um Web Service e quero mapear uma classe PedidoWs para a minha classe Pedido e para manter simples, digamos que as propriedades tem os mesmos nomes. Veja o diagrama abaixo:

Classes

Para fazer um mapeamento num cenário desses é bem simples, criamos o mapeamento assim:

Mapper.CreateMap();

E depois para usarmos o mapeamento basta executar:

Mapper.Map(meuObjetoDoTipoPedidoWs);

Esse é o padrão que vamos usar para mapear todos os objetos independentemente de como o mapeamento for criado.

Agora imagine que existam propriedades iguais, mas com nomes diferentes. Por exemplo, imagine que no PedidoWs existe uma propriedade chamada Cliente_ID ao invés de ClienteId, o mapeamento ficaria assim:

Mapper.CreateMap()
     .ForMember(c => c.ClienteId, opt => opt.MapFrom(x => x.Cliente_ID));

Veja, todos os outros atributos serão mapeados normalmente, apenas o ClienteId que foi declarado de forma explícita de onde vem.

Um outro caso importante é quando temos que fazer alguma conversão. Por exemplo, digamos que no nosso caso a data vem do web service em formato de string e temos que converter para DateTime. Faríamos assim:

Mapper.CreateMap()
     .ForMember(c => c.DataPedido, opt => opt.ResolveUsing()
         .FromMember(x => x.DataPedido);

Para usar o ResolveUsing precisamos definir a classe DateTimeResover e eu fiz dessa forma:

public class DateTimeResolver : ValueResolver
{
    protected override DateTime ResolveCore(string source)
    {
        return DateTime.Parse(source);
    }
}

Para finalizar, vale dizer que essa configuração dos mapeamentos pode ser demorada, então não é uma boa idéia executar as configurações toda a vez. O que eu faço, já que uso isso para a web a maior parte do tempo, é no meu Application_Start dentro do global.asax, executar essas configurações, assim elas são feitas apenas uma vez.

Existem várias outras opções de configuração para o AutoMapper e no site deles tem vários exemplos, vale a pena conferir. Aqui, foi só pra dar um gostinho.