En el post anterior vimos que comenzamos a ver utilizar la librería Mapster la cual nos permite realizar reasignaciones de un objeto a otro. Veamos cómo hacer referencia a librería, creamos una proyecto demo con algunas clases, vismo como asignar objeto y colecciones y creamos api que consumimos desde el navegador
En este post veremos algunas funcionalidades más avanzadas que no harán la vida más sencilla al reasignar objetos. La primera que veremos será la customización de los mapeos. Necesitaremos agregar una clase que nos permitirá configurar los mapeos. Para esto agregaremos Configs y dentro crearemos la clase estática MaspterConfig:
public static class MapsterConfig { public static void RegisterMapsterConfiguration(this IServiceCollection services) { } }
Debemos agregar el método RegisterMapsterConfiguration que será el método que invocamos en nuestro program.css para que inyecte el servicio y quede disponible. Agreguemos la siguiente línea:
builder.Services.RegisterMapsterConfiguration();
Comencemos a configurar. Supongamos que en lugar de devolver el objeto PersonaDto, del ejemplo del post anterior, que tenía las propiedades Título, Nombre y Apellido. Si queremos mostrar el nombre en una sola línea, deberíamos unir estas propiedades. ¿No sería mejor devolver un objeto con el nombre completo?. Para solucionar este problema primero crearemos la clase PersonaSimpleDto:
public class PersonaSimpleDto { public string? NombreCompleto{ get; set; } }
Lo siguiente será agregar la configuración de asignación, veamos el código y luego entraremos más en detalles:
public static void RegisterMapsterConfiguration(this IServiceCollection services) { TypeAdapterConfig<Persona, PersonaSimpleDto> .NewConfig() .Map(dest => dest.NombreCompleto, src => $"{src.Titulo} {src.Nombre} {src.Apellido}"); TypeAdapterConfig.GlobalSettings.Scan(Assembly.GetExecutingAssembly()); }
Tenemos TypeAdapterConfig, esta es una clase genérica que nos permite crear un mapeo entre 2 objetos, en este caso, Persona y PersonaFullNameDto. Luego creamos la configuración para ese par. Por último el método path el cual nos permite pasarle el destino y el origen de donde tomaremos los valores para unir. La última línea de nuestro método que busca en los ensamblados y lo registra.
Vamos a crear un nuevo método en nuestro controlador Persona que llamaremos CustomMapPersonaFullNameDto.
[HttpGet("GetPersonaFullName")] public IActionResult GetPersonaFullName() { var persona = SeedData.CrearPersona(); var personaSimpleDto= persona.Adapt<PersonaSimpleDto>(); return Ok(personaSimpleDto); }
Veamos el resultado desde la UI de Swagger:
Es posible tener condicionales los cuales permiten cambiar el valor que deseamos dependiendo de alguna situación que necesitemos. El método Map, además de aceptar 2 parámetros, puede aceptar un tercero donde le indicaremos la condición a evaluar en el origen. Si la condición no se cumple asignará null o un valor por default.
Supongamos que en nuestro objeto de full deseamos devolver la edad y no la fecha de nacimiento. Primero agregaremos la propiedad Edad.
public int? Edad { get; set; }
Lo siguiente será agregar en nuestra clase RegisterMapsterConfiguration, luego del map del nombre completo, otro map con la condición.
.Map(dest => dest.Edad, src => DateTime.Now.Year - src.FechaNacimiento.Value.Year, srcCond => srcCond.FechaNacimiento.HasValue);
Veamos el resultado:
Es posible ignorar algún miembro si lo deseamos. Mapster asigna las propiedades por sus nombres sin configuración de forma predeterminada. Si ignoramos algún miembro devolverá el valor null. Agreguemos un nuevo mapeo a nuestra clase:
TypeAdapterConfig<Persona, PersonaDto> .NewConfig() .Ignore(dest => dest.Titulo);
El resultado es:
Existe otro camino, podemos decorar la propiedad y funcionara de la misma manera que en el config.
[AdaptIgnore] public string Titulo { get; set; }
Por otro lado tenemos disponibles 2 métodos IgnoreNonMapped y IgnoreIf. El primero omitió los miembros que no están en la configuración, el segundo, no permite crear una condición para asignar. Investigarlos son muy interesantes.
Cuando configuramos un par en nuestro mapeo se realiza de izquierda a derecha. Nuestro ejemplo es Person a PersonDto. Si queremos configurar de PersonDto a Person ¿deberíamos agregar otro mapeo? No, no es necesario. Existe un método llamado TwoWays el cual nos permite mapear de ida y vuelta.
TypeAdapterConfig<Persona, PersonaDto> .NewConfig() .Ignore(dest => dest.Titulo) .TwoWays();
Conclusiones
En este post aprendimos algunas funcionalidades más avanzadas de la librería Mapster. Vimos cómo crear mapeo customizados, usar condiciones y cómo ignorar miembros. Estas librerías poseen muchas más funcionalidades como detectar eventos antes y después del mapeo o mapeo de objetos complejos. Puedes consultar la documentación si estás interesado. Documentación