9.01.2010

ValueResolver<T> y AutoMapper

En el post pasado acerca de AutoMapper, se expuso como hacer resúmenes de datos – sumatorias en este caso – utilizando Automapper. Entre los correos y comentarios que me llegaron acerca del post,  hubieron varios que me indicaban que la conversión en la configuración del mapeo, era un código un poco complicado de entender y dificil de dar mantenimiento, por lo cual me decidí a escribir este post en el cual explico como hacerlo de una forma “más mantenible" y entendible”.

AutoMapper tiene una clase abstracta llamada ValueResolver la cual nos permite configurar como queremos mapear a un tipo destino. La definición de esta clase dentro de AutoMapper es la siguiente:

using System;

namespace AutoMapper
{
public abstract class ValueResolver<TSource, TDestination> : IValueResolver
{
protected ValueResolver();

public ResolutionResult Resolve(ResolutionResult source);
protected abstract TDestination ResolveCore(TSource source);
}
}



Esta clase nos permite definir el tipo destino y el tipo fuente a través de la definición genérica de la clase. Además tiene un método crucial < ResolverCore> el cual debe de ser implementado cuando se utiliza al clase abstracta; este método es el que va a contener la lógica que nosotros queramos agregarle a la conversión de nuestros tipos. En nuestro caso, queremos tener un poco mas de control respecto a la forma en que se mapea el campo TotalDeHorasDelProyecto y CostoTotalDelProyecto, los cuales están definidos como atributos en la clase Resumen del Proyecto. Para lograr una configuración de la conversión un poco mas sencilla, vamos a crear un par de clases CostTotalDelProyecto y TotalDeHorasDelProyecto las cuales van a implementar el metodo ResolveCore. La definición de las clases es la siguiente:



public class TotalDeHorasDelProyecto : ValueResolver<Proyecto, double>
{

protected override double ResolveCore(Proyecto source)
{
return source.TareasDelProyecto.Sum(tarea => tarea.HorasAsignadas);
}

}

public class CostTotalDelProyecto : ValueResolver<Proyecto, double>
{

protected override double ResolveCore(Proyecto source)
{
return source.TareasDelProyecto.Sum(tarea => tarea.HorasAsignadas * tarea.CostoXHora);
}
}



Como se ve en el código anterior, en la implementación del método ResolveCore se codifico el código que antes estaba en la configuración del Mapeo. Ahora procedemos a reconfigurar nuestro mapeo en la aplicación de la siguiente manera:



Mapper.CreateMap<Proyecto, ResumenDelProyecto>()
.ForMember(dest => dest.NombreProyecto, src => src.MapFrom(fuente => fuente.Nombre))
.ForMember(dest => dest.TotalHorasDelProyecto, opt => opt.ResolveUsing<TotalDeHorasDelProyecto>())
.ForMember(dest => dest.CostoTotalEnHorasDeProyecto, opt => opt.ResolveUsing<CostTotalDelProyecto>());

var cp = Mapper.Map<Proyecto, ResumenDelProyecto>(p);



Analizando el código en negrita, vemos que estamos utilizando el método ResolveUsing<T> de AutoMapper, el cual nos permite indicarle el tipo de alguna clase que tenga implementada la clase abstracta ValueResolver, esto por cuanto el método ResolveUsing va a invocar por contrato la implementación del método ResolveCore provisto por la clase enviada como parámetro en T.



Technorati Tags: ,

1 comentario:

Anónimo dijo...

Excelente explicación y muy útil. Gracias.