Cuando trabaje en aplicaciones, a menudo necesitará convertir un tipo en otro. El mapeo de objetos es el proceso de mapear un objeto de origen a un objeto de destino, donde los tipos de objetos de origen y de destino pueden diferir.
Por ejemplo, es posible que necesite crear una instancia de una clase a partir de una instancia de otra clase y luego copiar los datos del objeto de origen al objeto de destino. Aunque hay muchos mapeadores de objetos disponibles para usar, es posible que necesites implementar tus propios mapeadores personalizados en determinadas situaciones. En este artículo, veremos cómo podemos implementar un asignador de objetos simple pero rápido en C#.
Crear un proyecto de aplicación de consola en Visual Studio
En primer lugar, creemos un proyecto de aplicación de consola .NET Core en Visual Studio. Suponiendo que Visual Studio 2022 esté instalado en su sistema, siga los pasos que se describen a continuación para crear un nuevo proyecto de aplicación de consola .NET Core.
- Inicie el IDE de Visual Studio.
- Haga clic en «Crear nuevo proyecto».
- En la ventana «Crear nuevo proyecto», seleccione «Aplicación de consola (.NET Core)» de la lista de plantillas que se muestran.
- Haga clic en Siguiente.
- En la ventana «Configura tu nuevo proyecto», especifica el nombre y la ubicación del nuevo proyecto.
- Haga clic en Siguiente.
- En la ventana «Información adicional», elija «.NET 8.0 (soporte a largo plazo)» como la versión del marco que le gustaría utilizar.
- Haga clic en Crear.
Usaremos este proyecto de aplicación de consola .NET 8 para implementar un asignador de objetos personalizado en las secciones siguientes de este artículo.
Beneficios y limitaciones de AutoMapper
La popular biblioteca de mapeo de objetos AutoMapeador es una excelente opción cuando desea mapear objetos de tipos incompatibles que tienen estructuras y nombres de propiedades similares. AutoMapper funciona muy bien con Entity Framework y escribir pruebas unitarias para tipos que usan AutoMapper es fácil. Una vez que haya configurado el mapeo entre los tipos, todo lo que tiene que hacer es escribir algunas líneas de código para probar las propiedades de la instancia de destino.
AutoMapper tiene varios beneficios que la convierten en una de las bibliotecas de mapas más utilizadas. Sin embargo, existen escenarios en los que deberíamos diseñar nuestros propios mapeadores de objetos personalizados en lugar de utilizar cualquier mapeador de objetos de terceros. Por ejemplo, si bien puede utilizar AutoMapper cuando el tipo de destino comprende un subconjunto aplanado de propiedades del tipo de origen, AutoMapeador no es adecuado para arquitecturas en capas complejas o arquitecturas que requieren una lógica de mapeo compleja entre objetos de tipos incompatibles.
Además, incluso si utiliza las últimas versiones de AutoMapper en su aplicación, existen inconvenientes de rendimiento que debe tener en cuenta. Si utiliza un asignador personalizado, tendrá un control más detallado sobre el diseño, lo que significa que puede escribir código para optimizar su asignador de objetos personalizado.
¿Qué es un mapeador de objetos personalizado? ¿Por qué lo necesitamos?
Un asignador de objetos personalizado es un componente que se utiliza para asignar o transformar un objeto de origen de un tipo en un objeto de destino de otro tipo. Puede utilizar un asignador de objetos personalizado para manejar una lógica de mapeo compleja y adaptar la lógica de mapeo para satisfacer las necesidades de su aplicación. Los mapeadores de objetos personalizados son útiles en escenarios donde es necesario mapear objetos de tipos incompatibles, es decir, cuando los tipos no tienen una relación de mapeo inherente.
Un asignador de objetos personalizado hace que el código sea más fácil de leer y mantener, y le permite reutilizar asignaciones entre proyectos. Reduce el tiempo de procesamiento desperdiciado en asignaciones manuales, proporcionando mayor flexibilidad para manejar escenarios complicados con colecciones de objetos anidados. Si utiliza un asignador de objetos personalizado, podrá asignar datos entre estructuras de datos complejas y diversas.
Puede evitar escribir lógica de mapeo innecesaria encapsulándola dentro de un asignador personalizado y luego reutilizándola en toda la aplicación. Al desarrollar los modelos y entidades que componen su aplicación, se recomienda emplear estructuras de datos livianas en lugar de pesadas para lograr una velocidad óptima.
Casos de uso típicos para un asignador de objetos personalizado
A continuación se presentan algunas razones por las que es posible que necesite utilizar un asignador de objetos personalizado:
- Soporte para mapear estructuras de datos incompatibles: un mapeador de objetos cierra la brecha entre dos clases o estructuras de datos que representan información similar pero tienen diferentes nombres de propiedades, tipos o estructuras. Puede aprovechar un asignador de objetos para copiar datos del objeto de origen al objeto de destino incluso si los objetos poseen nombres o estructuras de datos similares o diferentes.
- Soporte para integración con componentes externos: cuando trabaje con API o bases de datos externas, es posible que necesite manejar modelos de datos que no están perfectamente alineados con sus modelos de datos internos. Puede utilizar un asignador de objetos personalizado para manejar la lógica de asignación entre diferentes sistemas y estructuras de datos, principalmente para tipos que tienen diferentes nombres de propiedad.
- Soporte para control de versiones: con el tiempo, su software evolucionará de acuerdo con los cambios en los requisitos comerciales. Estos requisitos pueden incluir la erradicación de tipos y modelos heredados y la introducción de nuevos tipos y modelos. Puede manejar fácilmente la transformación a la nueva versión de sus modelos si utiliza un asignador personalizado en lugar de un asignador de terceros como AutoMappеr.
- Optimización del rendimiento: un asignador personalizado le permite escribir código de optimización del rendimiento en sus tipos y controlar qué propiedades se asignan, lo que reduce la sobrecarga innecesaria y mejora el rendimiento. Además, puede escribir código personalizado para comprimir o descomprimir datos, reduciendo así el ancho de banda de la red y mejorando el rendimiento.
- Procesamiento específico de dominio: un asignador personalizado también le permite escribir código para reglas y transformaciones específicas de dominio en sus tipos. Por ejemplo, es posible que necesite realizar operaciones comerciales específicas, ejecutar ciertas reglas comerciales o realizar conversiones de datos que sean específicas de su dominio de aplicación. En tales casos, puede aprovechar un asignador de objetos personalizado para incorporar código para reglas, conversiones de datos y transformaciones específicas del dominio.
Un ejemplo de asignador de objetos personalizado en C#
Profundicemos ahora en un poco de código. Considere las siguientes dos clases.
public class AuthorModel { public int Id { get; set; } public string FirstName { get; set; } public string LastName { get; set; } public string Address { get; set; } } public class AuthorDTO { public int Id { get; set; } public string FirstName { get; set; } public string LastName { get; set; } public string Address { get; set; } }
La siguiente lista de código implementa un asignador de objetos personalizado que asigna un objeto de origen a un objeto de destino.
public class CustomObjectMapper { public TDestination Map<TSource, TDestination>(TSource sourceObject) { var destinationObject = Activator.CreateInstance<TDestination>(); if (sourceObject != null) { foreach (var sourceProperty in typeof(TSource).GetProperties()) { var destinationProperty = typeof(TDestination).GetProperty (sourceProperty.Name); if (destinationProperty != null) { destinationProperty.SetValue (destinationObject, sourceProperty.GetValue(sourceObject)); } } } return destinationObject; } }
Puede utilizar el siguiente fragmento de código para utilizar el asignador personalizado. Observe cómo se ha utilizado el método Map genérico para asignar una instancia de tipo AuthorModel a una instancia de AuthorDTO.
var source = new AuthorModel(); source.Id = 1; source.FirstName = "Joydip"; source.LastName = "Kanjilal"; source.Address = "Hyderabad, India"; CustomObjectMapper mapper = new CustomObjectMapper(); var destination = mapper.Map<AuthorModel, AuthorDTO>(source); Console.WriteLine("Id = {0}, First Name = {1} Last Name = {2} Address = {3}", destination.Id, destination.FirstName, destination.LastName, destination.Address); Console.ReadLine();
Utilice un asignador de objetos personalizado en métodos de acción en C#
En esta sección, examinaremos cómo utilizar nuestro asignador personalizado en métodos de acción. Primero cree una interfaz llamada IAuthorRepository e ingrese el siguiente código.
public interface IAuthorRepository { public AuthorDTO GetAuthor(int id); }
La clase AuthorRepository implementará esta interfaz. Entonces, cree una nueva clase llamada AuthorRepository y escriba el siguiente código allí.
public class AuthorRepository : IAuthorRepository { private readonly CustomObjectMapper _customObjectMapper; public AuthorRepository(CustomObjectMapper customObjectMapper) { _customObjectMapper = customObjectMapper; } public AuthorDTO GetAuthor(int id) { var sourceObject = GetAuthorInstance(id); var destinationObject = _customObjectMapper.Map<AuthorModel, AuthorDTO>(sourceObject); return destinationObject; } }
Ahora, cree otra clase llamada AuthorController y reemplace el código generado automáticamente con la siguiente lista de códigos.
public class AuthorController : ControllerBase { private readonly IAuthorRepository _authorRepository; public AuthorController(IAuthorRepository authorRepository) { _authorRepository = authorRepository; } public IActionResult GetAuthor(int id) { var author = _authorRepository.GetAuthor(id); //Write your code here based on specific business rules return Ok(author); } }
Tenga en cuenta que en ambas clases se ha utilizado la inyección de dependencias para inyectar dependencias en los constructores. La clase AuthorRepository encapsula todas las llamadas al asignador personalizado. Es por eso que no hemos escrito ningún código para acceder o utilizar el asignador personalizado en la clase AuthorController.
La clase AuthorController recupera datos de la base de datos utilizando una instancia de AuthorRepository, es decir, no necesita interactuar directamente con la base de datos. En otras palabras, los métodos de acción de su controlador no deben contener código para realizar operaciones CRUD directamente en la base de datos.
El mapeo de objetos no siempre es fácil
El uso de un asignador de objetos personalizado le permite crear bases de código más limpias y fáciles de mantener y optimizar el acceso y la transferencia de datos. Al aprovechar la flexibilidad y eficiencia de los mapeadores de objetos personalizados, puede crear soluciones que contengan una lógica de mapeo adaptada a sus requisitos específicos.
Dicho esto, puede evitar el uso de un asignador de objetos personalizado si el mapeo es trivial, porque será más fácil realizar el mapeo sin él. La conclusión es que tienes muchos patrones y enfoques de diseño para elegir. Utilice AutoMapper cuando pueda y aproveche los mapeadores personalizados cuando lo necesite.
Copyright © 2024 IDG Communications, Inc.