martes, 23 de junio de 2015
Los filtros de MVC, o action filters, han sido potenciados en la versión 6 del framework añadiéndoles características que en versiones anteriores no estaban disponibles de serie en la plataforma pero que han sido muy solicitadas por los desarrolladores. Hace algún tiempo vimos una de ellas, los filtros asíncronos, y hoy veremos que la inyección de dependencias en filtros también es ya una realidad.
El problema con la inyección de dependencias en los filtros es la instanciación de éstos, pues al definirse en forma de atributos de .NET no puede realizarse de forma controlada, o al menos lo suficientemente controlada como para poder inyectarles parámetros en el constructor o cargar sus propiedades desde un contenedor de inversión de control. Y aunque los filter providers aportaron alguna solución vía los contenedores IoC más populares, aún no eran una solución todo lo limpia que debería ser.
Pero como decía David Wheeler, “Cualquier problema en ciencias de la computación puede ser solucionado con otro nivel de indirección”… y eso mismo han debido pensar la gente del equipo de ASP.NET en Microsoft, cuando la solución que han dado consiste, básicamente, en crear un filtro capaz de instanciar otros filtros usando el contenedor de servicios integrado :)
Veámoslo con un ejemplo. El siguiente código implementa un filtro asíncrono de MVC 6 cuya misión es almacenar en un log las acciones ejecutadas por los usuarios:
Observad que el almacenamiento del log se realiza utilizando un servicio externo, una instancia de
Este filtro no podemos aplicarlo directamente sobre nuestras acciones o controladores, pues el framework no podría crear una instancia satisfaciendo sus dependencias. Lo que tenemos que hacer en este caso es registrarlo en el contenedor de dependencias de ASP.NET Core, tarea que se hace en el método
En esta porción de código fijaos estamos registrando también la clase
Por último, ya sólo tenemos que aplicar el filtro a los controladores o acciones que nos interesen. Para ello, en lugar de hacerlo directamente, utilizaremos el filtro
Y eso es todo :) Como hemos podido ver, algo que en versiones anteriores era bastante complicado, o incluso imposible, como la inyección de dependencias en constructores de filtros, tiene solución trivial en el nuevo ASP.NET Core MVC 1.0.
Publicado en Variable not found.
El problema con la inyección de dependencias en los filtros es la instanciación de éstos, pues al definirse en forma de atributos de .NET no puede realizarse de forma controlada, o al menos lo suficientemente controlada como para poder inyectarles parámetros en el constructor o cargar sus propiedades desde un contenedor de inversión de control. Y aunque los filter providers aportaron alguna solución vía los contenedores IoC más populares, aún no eran una solución todo lo limpia que debería ser.
Pero como decía David Wheeler, “Cualquier problema en ciencias de la computación puede ser solucionado con otro nivel de indirección”… y eso mismo han debido pensar la gente del equipo de ASP.NET en Microsoft, cuando la solución que han dado consiste, básicamente, en crear un filtro capaz de instanciar otros filtros usando el contenedor de servicios integrado :)
Veámoslo con un ejemplo. El siguiente código implementa un filtro asíncrono de MVC 6 cuya misión es almacenar en un log las acciones ejecutadas por los usuarios:
Observad que el almacenamiento del log se realiza utilizando un servicio externo, una instancia de
IUserLoggingServices
inyectada en el constructor del filtro.Este filtro no podemos aplicarlo directamente sobre nuestras acciones o controladores, pues el framework no podría crear una instancia satisfaciendo sus dependencias. Lo que tenemos que hacer en este caso es registrarlo en el contenedor de dependencias de ASP.NET Core, tarea que se hace en el método
ConfigureServices()
de la clase Startup
:En esta porción de código fijaos estamos registrando también la clase
IUserLoggingServices
para asegurar que la dependencia de nuestro filtro pueda resolverse correctamente.Por último, ya sólo tenemos que aplicar el filtro a los controladores o acciones que nos interesen. Para ello, en lugar de hacerlo directamente, utilizaremos el filtro
ServiceFilter
, que actuará como factoría creando la instancia del filtro que le suministremos como parámetro, utilizando el contenedor de dependencias.
Y eso es todo :) Como hemos podido ver, algo que en versiones anteriores era bastante complicado, o incluso imposible, como la inyección de dependencias en constructores de filtros, tiene solución trivial en el nuevo ASP.NET Core MVC 1.0.
Publicado en Variable not found.
4 Comentarios:
Creo que esta solución es mucho más simple y mejor: http://blog.ploeh.dk/2014/06/13/passive-attributes/
El equipo de ASP.NET esta haciendo muy buen trabajo, pero muchas chanchadas en lo que a inyección de dependencias se refiere.
Hola!
Quizás lo único que me parece raro de la solución de Seemann es que no hay una "trazabilidad explícita" desde la clase del filtro pasivo hacia la implementación real, pero como enfoque es bastante válido. En cualquier caso, esta solución no requeriría gran cosa por parte del framework y es compatible con lo que se está haciendo, por lo que siempre podríamos tirar por este camino.
El approach actual en ASPNET (ojo, aún puede cambiar) del filtro "factoría" es sin duda raro, aunque probablemente hayan optado por él por lo parecido que resulta a las soluciones actuales y lo sencillo de aplicar que es. Creo que es mucho más fácil y directo poner un atributo ServiceFilter directamente en su lugar de aplicación que crear un filtro pasivo, un filtro "activo" y registrarlo como global.
Pero bueno, con el tiempo iremos viendo las ventajas e inconvenientes tanto de este enfoque como de los enfoques alternativos, y seguro que al final nos quedamos con el bueno :)
Está claro que el asunto está en dónde y cómo colocar la indirección para conseguir llegar al objetivo, porque es la única forma de salvar la limitación del uso de atributos para los filtros.
Muchas gracias por comentar y por el aporte!
Hola,
Coincido con lo que comenta Juan, me parece una solución más limpia.
Incluso optando por el enfoque de ASPNET, ¿tiene algún sentido que UserLoggerFilterAttribute herede de ActionFilterAttribute?
A simple vista, parecería más lógico tener un interface IFilterAttribute y que lo implementasen tanto ActionFilterAttribute (para los filtros "de toda la vida") como los nuevos filtros "pasivos" con dependencias, pero a lo mejor se me escapa algo.
Un saludo,
Juanma.
Hola, Juanma!
Supongo que el hecho de heredar tiene sentido para forzar el cumplimiento de un contrato (como el impuesto por el interfaz IActionFilter o similar) y por proporcionar alguna funcionalidad base, como el acceso al contexto o algo así. Pero como están haciendo con los controladores, también encajaría un POCO puro.
Gracias por comentar!
Enviar un nuevo comentario