2008-12-14 11 views
28

Creo que la respuesta a esta pregunta es tan obvia que nadie se ha molestado en escribir sobre esto, pero es tarde y realmente no puedo entender esto.Uso de contenedores IoC; específicamente Windsor

He estado leyendo en contenedores IoC (Windsor en este caso) y me falta cómo hablas con el contenedor desde varias partes de tu código.

Obtuve DI, he estado haciendo ma's pobres DI (constructores vacíos llamando a constructores de inyección sobrecargados con implementaciones de parámetros predeterminadas) durante algún tiempo y puedo ver completamente el beneficio del contenedor. Sin embargo, me falta una pieza vital de información; ¿cómo se supone que debes hacer referencia al contenedor cada vez que necesites un servicio de él?

¿Creo un ataque global único que paso por alto? ¡Seguramente no!

Sé que debería llamar a esto:

WindsorContainer container = new WindsorContainer(new XmlInterpreter()); 

(por ejemplo) cuando quiero cargar mi config XML, pero entonces, ¿qué hago con el contenedor? ¿La creación de un nuevo contenedor cada vez que persiste la configuración cargada a través de algunos majicks estáticos internos o de lo contrario, o tengo que volver a cargar la configuración cada vez (supongo que no, o lifecycles couldnt).

No comprender esto me impide trabajar en cómo funcionan los ciclos de vida, y seguir adelante con el uso de algunos COI awsomeness

Gracias,

Andrew

Respuesta

24

El 99% de los casos es una instancia de contenedor por aplicación. Normalmente lo inicializa en Application_Start (para una aplicación web), like this.

Después de eso, depende realmente del consumidor del contenedor. Por ejemplo, algunos frameworks, como Monorail y ASP.NET MVC le permiten interceptar la creación de instancias (los controladores en este caso), por lo que solo registra los controladores y sus dependencias en el contenedor y eso es todo, cada vez que recibe una solicitud el contenedor se encarga de inyectar a cada controlador sus dependencias. Ver por ejemplo this ASP.NET MVC controller. En estos marcos, casi nunca necesita llamar o incluso hacer referencia al contenedor en sus clases, que es el uso recomendado.

Otros marcos no dejar que se obtiene en el proceso de creación fácilmente (como formularios web) por lo que tiene que recurrir a trucos como this one, o tirón las dependencias necesarias (es decir, una llamada explícita del contenedor). Para extraer dependencias, use una puerta de enlace estática al contenedor como this one o la descrita por maxnk. Tenga en cuenta que al hacer esto, en realidad está usando el contenedor como un Localizador de servicios, que no desacopla tanto como la inversión del control.(vea la diferencia here y here)

Espero que esto despeje sus dudas.

+0

En realidad, muchos entornos tienen una clase de "raíz global" en alguna parte, si la buscas. Silverlight y WPF tienen la clase App (código subyacente para App.xaml) que es un lugar tan bueno como cualquier otro para anclar un contenedor IOC. Para WPF y Silverlight más grandes, definitivamente desearía ver Prism, que proporciona herramientas de estructuración a gran escala y Unity, que es el contenedor IOC de MS. –

+0

@Cylon: y ASP.NET tiene Application_Start(), pero eso no significa que le permita interceptar la creación de objetos. Solo significa que tienes un lugar para configurar el contenedor. –

+0

ASP.NET WebForms La creación de páginas se puede interceptar y las páginas se 'crean' para DI al hacer una PageHandlerFactory y registrar la implementación en web.config. Ver http://www.cuttingedge.it/blogs/steven/pivot/entry.php?id=81#comm –

0

estoy usando una implementación de este interfaz:

public interface IResolver 
{ 
    object Resolve(Type type); 
    object Resolve(string name); 

    T Resolve<T>() where T : class; 
    T Resolve<T>(string name) where T : class; 
} 

que en realidad está envuelto en clase estática global, por ejemplo:

public static class Resolver // : IResolver 
{ 
    private static IResolver _current; 

    public static object Resolve(Type type) 
    { 
     return Current.Resolve(type); 
    } 

    public static object Resolve(string name) 
    { 
     return Current.Resolve(name); 
    } 

    public static T Resolve<T>() where T : class 
    { 
     return Current.Resolve<T>(); 
    } 

    public static T Resolve<T>(string name) where T : class 
    { 
     return Current.Resolve<T>(name); 
    } 

    private static IResolver Current 
    { 
     get 
     { 
      if (_current == null) 
      { 
       _current = new SpringResolver(); 
      } 

      return _current; 
     } 
    } 
} 

También trato de seguir la regla simple: use la clase Resolver como menos posible, en su lugar, inyecte servicios en los objetos que necesitan esos servicios.

+4

-1 para recomendar un localizador de servicios. –

+0

Sin embargo (me doy cuenta de que llego tarde al juego aquí), si en lugar de tener Resolver sea una clase estática global, y la pasa como un argumento inyectado a objetos que necesitan crear sus propios objetos, entonces se convierte en una Fábrica Abstracta , que es mejor que un localizador de servicios, ¿no? –

3

En general, desea mantener solo una instancia durante toda la vida de la aplicación. Lo que hago la mayoría del tiempo es inicializar el contenedor cuando se inicia la aplicación, y luego uso las fábricas tipeadas para extraer objetos sin conocimiento del contenedor.

Otro enfoque popular es envolver la instancia del contenedor con la clase estática y usar esa clase estática para acceder a su contenedor (singleton). Puede encontrar un ejemplo de eso en la biblioteca Rhino.Commons de Ayende here. Sin embargo, este enfoque tiene graves inconvenientes y debe evitarse.

+0

"Lo que hago la mayoría del tiempo es inicializar el contenedor cuando se inicia la aplicación, y luego se lo paso a quienes lo necesitan." Me gusta esta idea. ¿Crees que podrías proporcionar un enlace o una muestra? –

+1

Eche un vistazo a la documentación de Windsor –

1

Como las otras respuestas aquí indican que hay muchas opciones, y de nuevo nos queda a nosotros mismos para descubrir qué es lo mejor en nuestro caso.

Dicho esto, la IMO que tiene un contenedor global al que se accede a través de su aplicación de alguna manera rompe el aislamiento ya que una gran cantidad de código ahora depende de la clase global. Además, para las aplicaciones que se dividen en varios ensamblados, el contenedor global debe estar disponible para todos estos ensamblajes.

Con Unity puede tener realmente un parámetro IUnityContainer en su constructor y el contenedor se inyectará automáticamente en la instancia cuando resuelva la clase. De esta forma, para los servicios que necesitan resolver otros servicios, los transfiere en el contenedor en lugar de forzar a la clase a hacer referencia a la clase externa.

No estoy seguro de cómo otros marcos admiten este escenario (Windsor inyectará IKernel).

+2

"para aplicaciones que se divide en varios ensamblajes, el contenedor global debe estar disponible para todos estos ensamblajes". No, uno de los principales puntos de cualquier contenedor IoC es no ser intrusivo. Windsor y otros logran y recomiendan eso. –

+0

@mausch - _how_ ¿logra eso, cuando parece haber una recomendación general de tener un contenedor Singleton compartido? ¿O estoy equivocado en esta suposición? No veo cómo mi respuesta contradice los puntos de IoC. Es solo cómo uno usa los marcos de la IoC, no los marcos en sí mismos, que se siente intrusivo. –

+1

Sí, hay un único contenedor, pero nadie debería saberlo, excepto el código de inicio y algún código de pegamento específico (es decir, un ASP.NET MVC ControllerFactory como http://mvccontrib.googlecode.com/svn/trunk/src/ MvcContrib.Castle/WindsorControllerFactory.cs). –

Cuestiones relacionadas