Saltar al contenido

Artículos, tutoriales, trucos, curiosidades, reflexiones y links sobre programación web ASP.NET Core, MVC, Blazor, SignalR, Entity Framework, C#, Azure, Javascript... y lo que venga ;)

17 años online

el blog de José M. Aguilar

Inicio El autor Contactar

Artículos, tutoriales, trucos, curiosidades, reflexiones y links sobre programación web
ASP.NET Core, MVC, Blazor, SignalR, Entity Framework, C#, Azure, Javascript...

¡Microsoft MVP!
martes, 11 de junio de 2013
ASP.NET MVCEn el primer post de la serie, vimos rápidamente en qué consiste la inyección de parámetros como fórmula para suministrar dependencias a una acción ASP.NET MVC, y realizamos una implementación sustituyendo unas pequeñas piezas del framework llamados action invokers que, como su nombre indica, son los responsables de invocar las acciones solicitadas por el usuario.

La idea es poder tener en nuestros controladores acciones que reciben directamente un interfaz que es resuelto justo en el momento de ejecutarla, es decir, algo así:
public ActionResult Products(IProductServices productsServices)
{
    return View(productsServices.GetAll());
}
Si intentamos ejecutar directamente esta acción en ASP.NET MVC, veremos que aparece un error indicando que no se puede crear una instancia de un interfaz. Lo que pretendemos conseguir, esta vez utilizando model binders, es que cuando se detecte un parámetro de este tipo, se resuelva la dependencia utilizando, por ejemplo, un contenedor IoC.

Pero antes de ver cómo, va una recomendación:
Disclaimer: como ya comenté en el artículo anterior, esta técnica no es especialmente recomendable en la mayoría de escenarios, puesto que puede ocultar dependencias y propiciar la aparición de controladores extensos y con demasiadas responsabilidades. Aún así, es un ejercicio interesante para conocer los mecanismos de funcionamiento internos del framework ASP.NET MVC.

Inyección de parámetros usando model binders

Los model binders son los componentes usado por el framework para obtener los valores para cada uno de los parámetros requeridos por una acción. Es decir, si en una acción tenemos un parámetro de tipo XYZ, será un model binder el que se encargue de buscar valores para el mismo en el contexto de la petición apoyándose a su vez en otros componentes llamados value providers, aunque para nuestros objetivos éstos no tienen importancia.

Por defecto, el proceso de binding de ASP.NET MVC es realizado por la clase DefaultModelBinder, aunque es posible, y además bastante fácil, crear una implementación alternativa y tomar el control durante este proceso simplemente heredando de ella y sobrescribiendo los métodos que nos interesen.

En particular, nos interesaría tomar el control en el momento de creación de las instancias de los parámetros de tipo referencia, que se encuentra en el método CreateModel() del binder. En ese punto podríamos comprobar si se está intentando construir un objeto a partir de un tipo interfaz, para proporcionárselo nosotros a través del dependency resolver y evitar el error que comentábamos antes.

Bien, pues tan sencillo como:
public class DependencyInyectorModelBinder: DefaultModelBinder
{
    protected override object CreateModel(ControllerContext controllerContext, 
                                          ModelBindingContext bindingContext, 
                                          Type modelType)
    {
        if (modelType.IsInterface)
        {
            return DependencyResolver.Current
                                     .GetService(modelType);
        }
        else return base.CreateModel(controllerContext, 
                                     bindingContext, modelType);
    }
}
El siguiente paso, como en otras ocasiones, sería hacer que el framework utilice este binder, para lo cual sólo tenemos que introducir el siguiente código para que sea ejecutado durante la inicialización del sistema:
ModelBinders.Binders.DefaultBinder = new DependencyInyectorModelBinder();
Observad que en este momento, cada vez que el binder se encuentre con un parámetro de tipo interfaz, acudirá al dependency resolver para intentar obtener una instancia. Lo último que tenemos que hacer sería encargarnos de dicha resolución.

En el post anterior vimos cómo hacerlo directamente implementando un dependency resolver personalizado, pero podemos hacerlo incluso de forma más fácil si utilizamos Unity o cualquier otro contenedor de inversión de control. En este caso, podríamos instalar el paquete Unity.Mvc y registrar nuestras clases de la forma habitual:
public static void RegisterTypes(IUnityContainer container)
{
    container.RegisterType<IProductServices, ProductServices>(
                    new PerRequestLifetimeManager());
    container.RegisterType<INotificationServices, NotificationServices>(
                    new PerRequestLifetimeManager());
}
A diferencia del artículo anterior, en este caso sí estaríamos gestionando correctamente el ciclo de vida de las dependencias, puesto que serían liberadas automáticamente al finalizar la petición. Lo único que no debemos olvidar es introducir también esta inicialización del contenedor en el proceso de arranque de la aplicación.

Resumen y próximos pasos

En este artículo hemos visto otra posibilidad para implementar la inyección de parámetros en acciones ASP.NET MVC, utilizando esta vez model binders, dado que son éstos los encargados de crear las instancias de las clases que son suministradas como parámetros a los métodos. Como siempre, hemos podido comprobar lo sencillo que resulta modificar la implementación que viene de serie en el framework para adaptarlo a nuestras necesidades.

Sin embargo, ciertamente es bastante trabajoso, y más aún teniendo en cuenta que hay ya herramientas que nos lo dan solucionado. En el próximo post, el último de la serie, veremos una de ellas ;)

El ejemplo desarrollado en este post podéis descargarlo desde Skydrive.

Publicado en Variable not found.

Aún no hay comentarios, ¡sé el primero!