Aprende en casa Ir a KODOTI
Aprende en casa KODOTI

Tutorial fácil de AutoMapper

Automapper nos facilita al vida al momento de mapear de manera automática las propiedades de un objeto.

Rodríguez Patiño, Eduardo
Rodríguez Patiño, Eduardo
2020-07-03 | 25,913 lecturas
Actualizado:

Hemos renovado completamente esta lección con las nuevas características que trae AutoMapper. En este ejemplo vamos a crear mapeos para reclutar nuevos postulantes a nuestra empresa de desarrollo de software y usaremos Net Core 2.0. El ejemplo debería funcionar sin problemas en NetFramework.

¿Qué es AutoMapper?

Como su nombre lo dice es un mapeador. Es decir, permite para los valores seteados en una clase A a una clase B evitando la tediosa manera de hacerlo manualmente.

[HttpPost]
public IActionResult Test1(ApplicantCreateViewModel user)
{
    var model = new User
    {
        Birthday = user.Birthday,
        Genre = user.Genre,
        Name = user.Name,
        Lastname = user.Lastname,
        Speciality = user.Speciality,
        Expertise = user.Expertise
    };

    // do something ..
    return NoContent();
}

Esto sería la manera manual y con AutoMapper se resume a las siguiente línea instalando previamente la versión más reciente del AutoMapper.

Paso #1

Con la nueva versión de AutoMapper la configuración es más sencilla, en el startup de nuestro proyecto configuremos lo que queremos mapear.

public void ConfigureServices(IServiceCollection services)
{
    services.AddMvc();

    Mapper.Initialize(cfg => {
    cfg.CreateMap();
    });
}

Paso #2

Realizamos el mapeo

[HttpPost]
public IActionResult Test1(ApplicantCreateViewModel user)
{
    var model = Mapper.Map(user);

    // do something ..
    return NoContent();
}

Lo que hace Automapper es hacer match con las propidades para poder pasar los valores del punto A al B.

Ejemplo más avanzado

Comunmente no solemos trabajar con nuestras clases de dominio por un tema de buenas prácticas. Por ejemplo los DTO o en nuestro ejemplo los ViewModelos hace referencia a los formularios y estos no necesariamente comparten las mismas propiedades que el dominio. Para mi ejemplo que he creado las siguientes clases y enums.

public class User
{
    public string Id { get; set; }
    public string Name { get; set; }
    public string Lastname { get; set; }
    public DateTime Birthday { get; set; }
    public bool Genre { get; set; }
    public decimal CurrentSalary { get; set; }
    public decimal SalaryPretension { get; set; }

    public UserSpecialityType Speciality { get; set; }
    public UserExpertise Expertise { get; set; }

    public int Probality { get; set; }
}

public class ApplicantCreateViewModel
{
    public string Name { get; set; }
    public string Lastname { get; set; }
    public DateTime Birthday { get; set; }

    public bool IsSingle { get; set; }
    public bool IsWorking { get; set; }

    public bool Genre { get; set; }
    public decimal Salary { get; set; }

    public UserSpecialityType Speciality { get; set; }
    public UserExpertise Expertise { get; set; }

    public bool Accept { get; set; }
}

public enum UserSpecialityType
{
    FrontEnd,
    Backend,
    FullStack
}

public enum UserExpertise
{
    Junior,
    SemiSenior,
    Senior
}

Partiendo de esto podrán ver que el ViewModel no es igual a la clase User, así que le diremos a AutoMapper que queremos un comportamiento personalizado para hacer el mapeo. Mi siguiente ejemplo tendrá dos casos especificos:

  • Nuestra clase de dominio "User" tiene un campo llamado SalayPrentesion el cual es las pretensiones salariales del postulante pero en nuestro ViewModel se llama Salary.
  • Hay un campo adicional que se llama "Probality", el cual es un entero y puede ii de 1 a 0. Siendo el 5 una probabilidad muy alta de que sea contratado.

Para calcular esto he construido una clase UserHelper donde hará la lógica.

public static class UserHelper
{
    public static int CalculateProbabilty(decimal salary, UserSpecialityType type, UserExpertise expertise)
    {
        var probality = 0;

        if (type == UserSpecialityType.Backend)
        {
            if (expertise == UserExpertise.Junior && salary > 2000) probality = 1;
            else if (expertise == UserExpertise.SemiSenior && salary > 5000) probality = 1;
            else if (expertise == UserExpertise.Senior && salary > 7000) probality = 1;

            else if (expertise == UserExpertise.Junior && salary < 1000) probality = 5;
            else if (expertise == UserExpertise.SemiSenior && salary < 4000) probality = 5;
            else if (expertise == UserExpertise.Senior && salary < 6000) probality = 5;

            else probality = 4;
        }

        if (type == UserSpecialityType.FrontEnd)
        {
            if (expertise == UserExpertise.Junior && salary > 1500) probality = 1;
            else if (expertise == UserExpertise.SemiSenior && salary > 4000) probality = 1;
            else if (expertise == UserExpertise.Senior && salary > 5500) probality = 1;

            else if (expertise == UserExpertise.Junior && salary < 1000) probality = 5;
            else if (expertise == UserExpertise.SemiSenior && salary < 3000) probality = 5;
            else if (expertise == UserExpertise.Senior && salary < 5000) probality = 5;

            else probality = 4;
        }

        if (type == UserSpecialityType.FullStack)
        {
            if(expertise == UserExpertise.Junior && salary > 3000) probality = 1;
            else if (expertise == UserExpertise.SemiSenior && salary > 6000) probality = 1;
            else if (expertise == UserExpertise.Senior && salary > 9000) probality = 1;

            else if (expertise == UserExpertise.Junior && salary < 2000) probality = 5;
            else if (expertise == UserExpertise.SemiSenior && salary < 4000) probality = 5;
            else if (expertise == UserExpertise.Senior && salary < 5000) probality = 5;

            else probality = 4;
            }

            return probality;
        }
}

El AutoMapper también tiene sus propios resolvedores llamados Custom Value Resolvers pero en mi caso quiero reutilizar esta lógica no solo para el mapeo de AutoMapper.

La configuración quedaría de la siguiente manera

Mapper.Initialize(cfg => {
    cfg.CreateMap()
    .ForMember(dest =>
        dest.SalaryPretension,
        opt => opt.MapFrom(src => src.Salary)
    )
    .ForMember(dest =>
        dest.Probality,
        opt => opt.MapFrom(src => UserHelper.CalculateProbabilty(src.Salary, src.Speciality, src.Expertise))
    );
});

El resto seguiría igual.

A los amantes del código limpio

En un proyecto real vamos a tener muchas clases de configuración para el automapper y que mejor manera de organizar el código mediante los Profile.

Creando la clase Profile donde irá la configuración de nuestro usuario

public class UserProfile : Profile
{
    public UserProfile()
    {
        CreateMap()
        .ForMember(dest =>
            dest.SalaryPretension,
            opt => opt.MapFrom(src => src.Salary)
        )
        .ForMember(dest =>
            dest.Probality,
            opt => opt.MapFrom(src => UserHelper.CalculateProbabilty(src.Salary, src.Speciality, src.Expertise))
        );
    }
}

La nueva configuración

Mapper.Initialize(cfg => {
    cfg.AddProfile();
});

De esta manera nuestro código de inicialización quedo más limpio y podemos crear un profile para cada clase del dominio ya que, en un proyecto real no solo usamos ViewModel, podemos usar N tipo de clases para mapear al dominio o vicervesa. Por ejemplo, como buena práctica cuando hacemos un listado no retornamos la clase de dominio, sino una clase del tipo DTO.


Estudia con nosotros

🚀 Mejora tus oportunidades laborales