martes, 11 de junio de 2013

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!
Enviar un nuevo comentario