AutoMapper (I) Flattening
Antes de escribir sobre este proyecto, he realizado una búsqueda en Geeks.ms y no he encontrado nada, así que empezaremos desde cero para que todo el mundo la conozca.
¿Qué es AutoMapper?
Básicamente y sin complicar mucho la definición, sirve para realizar mapeos entre objetos .NET usando fluent interfaces y convention over configuration, o lo que es lo mismo, interfaces fluidas y convenciones predifinidas en su API en vez del uso de ficheros de configuración, aunque esto no quita que podamos configurarlo libremente como veremos más adelante. Nos evita tareas repetivivas y aburridas como es la transformación de objetos entre capas, aplicaciones… de manera automática.
Vamos a ver un primer ejemplo de la funcionalidad más básica o lo que se conoce como Flattening (Podríamos traducirlo como proyección plana pero suena fatal y creo que en este caso queda mejor mapeo por defecto, es decir basandonos en sus convenciones)
Para instalar AutoMapper podéis usar NuGet:

Partimos de unas entidades de dominio de ejemplo como es una factura, el cliente, las líneas de factura y los productos:
public class Invoice
{
public Guid Id { get; set; }
public Customer Customer { get; set; }
public List<InvoiceLine> Lines { get; set; }
public decimal GetTotal()
{
return Lines.Sum(l => l.Product.Price*l.Quantity);
}
}
public class Customer
{
public Guid Id { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
}
public class InvoiceLine
{
public Guid Id { get; set; }
public Product Product { get; set; }
public int Quantity { get; set; }
public decimal GetTotal()
{
return Product.Price*Quantity;
}
}
public class Product
{
public decimal Price { get; set; }
public string Name { get; set; }
}
Tenemos como requerimiento comunicarnos con un ERP y enviarle cierta información, como es el Id de la factura, el Id del cliente y el total de la factura. Para estos casos no vamos a enviar información de nuestro dominio al servicio del ERP que nada tiene que ver con nuestra aplicación, y además solo hace falta enviar un objeto con 3 propiedades y para ello vamos a usar un
DTO:
public class InvoiceDto
{
public Guid Id { get; set; }
public Guid CustomerId { get; set; }
public decimal Total { get; set; }
}
Vamos a crear un test de prueba para ver como funciona AutoMapper:
[TestMethod]
public void Transform_Invoice_To_InvoiceDto()
{
var invoiceId = new Guid("C30B6A72-C822-11E0-99CD-3B224824019B");
var customerId = new Guid("B4228FF2-C824-11E0-844C-6E244824019B");
var invoice = new Invoice
{
Id = invoiceId,
Customer = new Customer
{
Id = customerId,
FirstName = "FirstName",
LastName = "LastName"
},
Lines = new List<InvoiceLine>
{
new InvoiceLine
{
Id = Guid.NewGuid(),
Product = new Product
{
Name = "MotherBoard",
Price = 120
},
Quantity = 20
},
new InvoiceLine
{
Id = Guid.NewGuid(),
Product = new Product
{
Name = "CPU",
Price = 230
},
Quantity = 10
}
}
};
Mapper.CreateMap<Invoice, InvoiceDto>();
var invoiceDto = Mapper.Map<Invoice, InvoiceDto>(invoice);
Assert.AreEqual(invoiceDto.Id, invoiceId);
Assert.AreEqual(invoiceDto.CustomerId, customerId);
Assert.AreEqual(invoiceDto.Total, invoice.Lines.Sum(l => l.Product.Price * l.Quantity));
}
Vamos a ver que estamos haciendo en el test por pasos:
- Creamos una factura de prueba.
- Con la función CreateMap de AutoMapper creamos un mapeo entre nuestra entidad de dominio Invoice y el DTO que vamos a enviar al ERP InvoiceDto.
- Acto seguido, hacemos uso de la función Map, indicando el tipo de dato de origen y el tipo de dato de destino y pasamos como parámetro nuestra factura de prueba y el se encargará de mapearla automaticamente a nuestro DTO y retornarlo.
- Por último, comprobamos que lo ha mapeado correctamente, que los ids coinciden y que el total es el mismo.
¿Donde está la magia?
Pues como dijimos antes, aquí no hay magia, AutoMapper ha usado lo que conocemos como convención frente a configuración, ha mapeado el Id de nuestra factura con el Id del DTO, para el caso del cliente ha usado la convención Clase.Propiedad (CustomerId –> Customer.Id) y para el Total ha buscado un método que se llame GetTotal.
Para el total también aplicaría si usasemos una propiedad tal que así:
public decimal Total
{
get { return Lines.Sum(l => l.Product.Price*l.Quantity); }
}
En el siguiente post veremos otro tipo de transformación Projection, donde vamos a configurar la API de AutoMapper para hacer algún mapeo más customizado
Hasta la próxima.