jueves, 26 de diciembre de 2013

Automapper

Quiero contarles acerca de Automapper para quienes no lo conozcan. Es una herramienta sencilla y que se puede obtener fácilmente desde Visual Studio con NuGet.

Automapper les facilitará la vida cuando necesiten convertir objetos de diferente tipo pero con estructuras similares y les ahorrará muchas líneas de código.

Consideremos el siguiente código:
/// <summary>
/// Copy ArticleDoc to new ArticleModel.
/// </summary>
/// <param name="doc">Article to copy</param>
/// <returns>A new ArticleModel object</returns>
public static ArticleModel ArticleDocToModel(ArticleDoc doc)
{
    if (doc == null)
        return null;

    // create model
    ArticleModel model = new ArticleModel();
    model.Id = doc.id.ToString();
    model.Title = doc.title;
    model.Introduction = doc.introduction;
    model.Content = doc.content;
    model.CreateDate = doc.createDate;
    model.ChangeDate = doc.changeDate;
    model.PubDate = doc.pubDate;
    model.PubStatus = doc.pubStatus;
    model.Categories = doc.categories;
    model.TwoColumns = doc.twoColumns;
    model.Favorite = doc.favorite;
    if (doc.author != null)
    {
        model.AuthorName = doc.author.name;
        model.AuthorTitle = doc.author.title;
    }
    if (doc.comments != null)
    {
        model.Comments = new List<CommentModel>();
        doc.comments.ForEach(c => model.Comments.Add(CommentModelHelper.CommentObjToModel(c)));
    }
    return model;
}

El procedimiento anterior se utiliza para obtener un objeto de tipo ArticleModel que proviene de otro objeto similar pero de tipo ArticleDoc. Lo que se quería con esta función era convertir "documentos" provenientes de una base de datos de documentos (MongoDB) en objetos convenientes para nuestro modelo MVC.

Lo tedioso del asunto era hacer el mapeo de los atributos uno a uno, atributos con características similares, nombre y tipo. Así que un día decidí buscar una manera mas sencilla de hacer este trabajo y me tropecé con Automapper. Así que, sin mas preámbulos, veamos cómo funciona:
/// <summary>
/// GET: /Order/Details/5
/// </summary>
/// <param name="id">The order id.</param>
/// <returns>The ActionResult.</returns>
public ActionResult Details(int id)
{
    var order = this.orderRepository.GetById(id);

    Mapper.CreateMap<Order, OrderViewModel>();
    var model = Mapper.Map<OrderViewModel>(order);
    
    return this.View(model);
}

Ya no se necesita hacer el mapeo manual de los atributos. Ahora simplemente se le da la orden a Automapper de crear un nuevo objeto de tipo OrderViewModel y copiar los atributos similares que se encuentran en el objeto de tipo Order. Con esto nos hemos ahorrado una gran cantidad de líneas de código.

Automapper también ofrece la posibilidad de crear nuestros propios "resolvers" para que los mapeos actúen como nosotros deseamos, pero esto lo pueden investigar ustedes en la página de Automapper. Yo solo cumplo con informarles de que existe esta herramienta que es muy sencilla de utilizar y que les pudiera resultar muy útil en alguna ocasión.

jueves, 5 de diciembre de 2013

Los IEnumerables

Hoy quiero escribir acerca de los IEnumerables debido a un problema de queries duplicados detectado por miniprofiler que tuve en uno de mis proyectos.

Nosotros estamos acostumbrados a trabajar con funciones que nos devuelven un IEnumerable con datos de la base de datos:
IEnumerable<Order> GetOrders(int take = 10, int skip = 0);

Supongamos que nuestra implementación de GetOrders es algo como:
public IEnumerable<Order> GetOrders(int take = 10, int skip = 0)
{
    return this.FindAll()
        .OrderByDescending(o => o.ID)
        .Skip(skip)
        .Take(take)
        .AsEnumerable();
}

La tendencia natural es suponer que cuando hacemos una llamada al procedimiento GetOrders obtenemos un objeto tipo lista que podemos iterar desde cualquier punto de nuestro código sin ningún problema... y realmente es así, pero tenemos que estar conscientes de que cada vez que iteramos sobre dicho objeto se realizará una nueva consulta en la base de datos.
var orders = orderServices.GetOrders();
// Hasta ahora no hay consultas a base de datos

var ordersList = orders.ToList();
// Se realizó una consulta en base de datos

foreach (var order in orders) {...}
// Se realizó nuevamente una consulta en base de datos

Y así sucesivamente, cada vez que "enumeramos" sobre el objeto "orders" de tipo IEnumerable estaremos realizando una consulta en la base de datos que quizás no era lo que esperábamos.

Lo mas recomendable, si vamos a usar un objeto de tipo IEnumerable que necesitamos iterar mas de una vez, es crear una lista que almacena sus datos en memoria y pueda ser iterada las veces que se necesite sin realizar consultas innecesarias a la base de datos. En el ejemplo anterior podemos utilizar "ordersList" en lugar de "orders" cuando hacemos la iteración "foreach", el resultado será el mismo pero de esta manera se evita realizar la consulta extra a la base de datos.

Otro problema que puede ser un poco mas difícil de detectar es cuando hacemos una llamada a un procedimiento que tiene un parámetro de tipo IEnumerable. Digo "difícil de detectar" porque en la mayoría de los casos se nos puede pasar el hecho de que ese procedimiento va a "enumerar" nuestro objeto (consultando a la base de datos) y nosotros, inocentemente, lo volvemos a enumerar en nuestro código, y por supuesto, generando una consulta duplicada a base de datos. El otro problema es que le estamos dando la exclusividad a dicho procedimiento de utilizar nuestro objeto IEnumerable sin que nosotros lo podamos enumerar en nuestro código (claro si no queremos hacer consultas duplicadas a base de datos). Lo que se recomienda en estos casos es crear procedimientos con otro tipo de parámetros distintos a IEnumerable, por ejemplo List, así estaremos pasando una lista en memoria que puede ser leída múltiples veces sin consultar a la base de datos.

Según tengo entendido, no estoy 100% seguro porque no utilizo Resharper, es que las últimas versiones de Resharper pueden detectar este tipo de problemas con los IEnumerables y da una advertencia: "Possible Multiple Enumeration of IEnumerable".

Con Miniprofiler se pueden observar las consultas que Entity Framework hace en la base de datos. Miniprofiler nos puede dar una advertencia en caso de que se hagan consultas duplicadas y también nos muestra en qué lugar de nuestro código se realizan las consultas a la base de datos, como se puede ver en la siguiente imagen, aquí se efectuaron 2 consultas en el Controller y una en el Render (o Vista).


Por último les invito a probar Miniprofiler y a jugar con enumerar los objetos tanto en la Vista como en el Controller para comparar los resultados.

jueves, 28 de noviembre de 2013

Introducción

Un saludo a todas aquellas personas interesadas en el desarrollo de sistemas web. Estoy creando este blog para relatar mis experiencias personales y dejar información que pueda servir de ayuda para aquellos que, al igual que yo, se han quedado "pegados" encontrando la solución a algún problema. Igualmente quiero aprender de otras personas sus experiencias y métodos que utilizan para resolver problemas. Sin mucho mas que decir, muchas gracias y pronto estaré publicando nuevas entradas al blog.

Saludos

Jonás Carreño