[ASP.NET MVC 3] Por qué IDependencyResolver no cumple con la filosofía de los IoC

No se si el título es muy adecuado, pero espero que leyendo esto y los artículos que menciono os quede más claro.

Tengo pendiente escribir una serie de sobre DI (Qué es, patrones, antipatrones…), pero de momento voy a escribir sobre este tema ya que el otro día por twitter lo estuve hablando con @pablonete sobre como implementar DI en ASP.NET MVC, Yo conocía desde la versión 1.0 la implentación de DI usando un IControllerFactory pero ví que en la versión 3 se había añadido la interfaz IDependecyResolver y parecía la manera correcta de hacerlo ahora, así que le pasé un enlace de como hacerlo así.

Peroooo, si leemos a los guruses hablar de temas de arquitecturas y mas concretamente sobre DI, como podemos leer en este artículo de Mark Seemann’s, los contenedores de dependencias disponen de 3 métodos para registrar, resolver y limpiar dependencias, Mark propone llamar a este patrón Register Resolve Release pattern basandose el el nombre de los métodos de CastleWindsor (En otros contenedores se utilizan otros nombres que a lo mejor pueden despistar un poco al programador, en el caso de Unity el método Release es TearDown)

En ASP.NET MVC 3 han añadido la interfaz IDependencyResolver para facilitarnos el uso de DI, pero esta interfaz carece de un método Release al contrario de IControllerFactory:

public interface IControllerFactory

{

    IController CreateController(RequestContext requestContext, string controllerName);

    void ReleaseController(IController controller);

}

public interface IDependencyResolver 

{    

    object GetService(Type serviceType);    

    IEnumerable<object> GetServices(Type serviceType);

}

Os recomiendo la lectura de este artículo de Mike Hadlow

The MVC 3.0 IDependencyResolver interface is broken. Don’t use it with Windsor.

Leyendo esto me asaltan las dudas de porque han hecho esto de esta manera la gente del equipo de MVC, si ya disponíamos desde la versión 1.0 de un mecanismo que al parecer se adapta mejor a estos patrones ¿no?

Espero opiniones al respecto y si vemos que esto da para rato, creamos una entrada en el Grupo de AUGES en LinkedIn.

Un saludo.

3 comentarios en “[ASP.NET MVC 3] Por qué IDependencyResolver no cumple con la filosofía de los IoC”

  1. Muy intersante Luis, la verdad es que es cierto que IDependencyResolver no se lleva muy bien con Windsor, pero, sin ser un experto en Windsor, creo que es por un detalle de implementación en su caso.

    Si tu diseñas la clase para que sea Disposable y la disposeas explicitamente no veo el problema.

    Es más, con Unity necesitas hacer una implementación explicita similar por que el TearDown no hace absolutamente nada por defecto. Unity no sabe nada de que instancias crea, por eso si creas instancias diposables tienes que liberarlas explicitamente o crear una ‘container extension’.

    De todos modos es cierto, IDependencyResolver no sirve en todos los casos… 🙁

  2. Por cierto en el ejemplo de Hadlow el ejemplo tiene un error en sí mismo.

    Su controller no implementa IDisposable si lo implementase MVC se encargaría de disposear el controller, eso está claro.

    El error es que su controller agrega un instancia disposeable y sin embargo no es disposeable. Si tu agregegas una clase disposeable la clase contenedora debe implmenentar IDesposable.

    Es algo que FxCop te apunta.

    Por eso, si usas IDependencyResolver e implmentas bien IDisposable no hay problema ninguno con IDependencyResolve… salvo que yo me haya perdido algo… que puede ser 🙂

  3. Hola, Luis.

    Interesante tema, pardiez 🙂

    No conozco en absoluto Windsor, por lo que no sé cómo controla el ciclo de vida de los objetos, pero con Unity es posible liberar los recursos de forma automática (o sea, llamar al Dispose() de los objetos gestionados por el contenedor). Por tanto, no sería necesario ningún “Release()” en el DependencyResolver, como se propone en el post citado.

    De hecho, se consigue muy fácilmente usando el componente Unity.Mvc3 (disponible en Nuget). Se trata de un pequeño componente que asocia a la petición actual un contenedor en el que va guardando instancias que debe liberar al finalizar el proceso de la misma, momento en el que llama a sus respectivos Dispose().

    Así, por ejemplo, si a un controlador se le inyecta una instancia de IMiServicio usando un contenedor Unity para resolver la dependencia, una vez procesada la petición ocurre lo siguiente:

    – En primer lugar, la propia factoría de controladores (o al menos la implementación por defecto DefaultFactoryController) es la encargada de invocar al método Dispose() del controlador. De hecho, la propia clase base Controller implementa IDisposable(), por lo que otra posibilidad es simplemente sobreescribir el método Dispose(bool) en nuestro controlador.

    Por tanto, coincidiendo con Rodrigo, el primer problema que tiene Hadlow es que, según la implementación de su controlador, no está usando recursos que desee liberar.

    – En segundo lugar, si el tipo concreto de IMiServicio (por ejemplo la clase MiServicio) implementa IDisposable y lo hemos registrado apropiadamente, al finalizar la petición se llamará a su método Dispose(). Es Unity.Mvc3 el que se encarga de realizar esta llamada de forma automática.

    El registro del tipo en la inicialización del contenedor para que esto ocurra debe ser como la siguiente, utilizando el gestor HierarchicalLifetimeManager:

    container.RegisterType(new HierarchicalLifetimeManager());

    Todo esto viene muy bien explicado en este post: http://devtrends.co.uk/blog/introducing-the-unity.mvc3-nuget-package-to-reconcile-mvc3-unity-and-idisposable, y se implementa en un periquete.

    En definitiva, su propio nombre lo indica: DependencyResolver. Sólo sirve para resolver dependencias, no para gestionar el ciclo de vida de los objetos. De esto, imho, debería ocuparse el contenedor correspondiente.

    Un saludo & gracias por hacernos reflexionar! 🙂

Deja un comentario

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *