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:

image

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:

  1. Creamos una factura de prueba.
  2. 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.
  3. 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.
  4. 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.
Published 12/10/2011 23:29 por Luis Ruiz Pavón
Archivado en: ,,
Comparte este post:
http://geeks.ms/blogs/lruiz/archive/2011/10/12/automapper-i-flattening.aspx

Comentarios

# re: AutoMapper (I) Flattening

Hola Luis,

AutoMapper va genial! En el post indicas uno de sus múltiples usos, el paso de objetos de datos a sistemas externos... otro uso que le podemos dar es para crear los ViewModels a partir de entidades del dominio en aplicaciones ASP.NET MVC3.

En este sentido me sorprendió bastante que no se pudiera hacer el mapeo inverso, es decir, pasar de ViewModel a entidades del dominio (tenéis la justificación de Jimmy Bogart aquí: lostechies.com/.../the-case-for-two-way-mapping-in-automapper)...

¿Necesitáis hacer el mapping ViewModel -> entidad del dominio? ¿Cómo lo hacéis? ¿"a manija"?

Buen post para introducir el flattering y a Automapper!

Salute!

Friday, October 14, 2011 9:16 AM por Josep Maria Camps i Riba