2010-10-11 16 views
6

El siguiente código es solo para fines de demostración.Castle Windsor IOC: paso de los parámetros del constructor a los componentes secundarios

Digamos que tengo 2 componentes (businessService y dataService) y una clase de UI.

La clase UI necesita un servicio comercial, businessService necesita un servicio de datos, y dataService se basa en un connectionString.

Formulario de la clase de interfaz de usuario en Necesito resolver el servicio de negocio, así que estoy escribiendo el código de abajo:

var service = container.Resolve<BusinessService>(new { dependancy = "con string 123" })); 

aviso de esa dependencia es el parámetro constructor connectionString.

Pero el código anterior no funciona, diciendo que dataService espera dependencia que no se satisfizo.

No se puede crear el componente 'Dataservice' ya que tiene dependencias para ser satisfecho. dataService está esperando las siguientes dependencias:

Teclas (componentes con claves específicas) - dependencia que no se ha registrado.

Así como una solución que estoy haciendo esto:

var service = container.Resolve<BusinessService>(new { dataService = container.Resolve<IDataService>(new { dependancy = "123" }) }); 

Pero desde el diseño, la codificación de estilo y muchos puntos de vista esto no es una buena manera de hacerlo.

Así que por favor si puede aconsejar por qué no está funcionando de la manera más simple o si tiene una mejor solución, por favor comparta.

+0

¿Puede proporcionar una definición breve pero completa de BusinessService, DataService y dependancy/connectionString que fallará? Elimine cualquier código que no contribuya directamente a reproducir el problema. –

+0

Es simple y directo –

+0

clase pública BusinessService {public BusinessService (DataService dataService) {}} –

Respuesta

6

El comportamiento que ves es por diseño.

Hay un par de maneras de abordar el problema, dependiendo de qué tan dinámico sea el valor que desea transmitir.

The documentation hace un trabajo bastante bueno que lo detalla, por lo que no voy a reiterarlo aquí.

actualización

Para claridad - Windsor no pasa argumentos en línea por la tubería resolución. La razón de esto es simple: al hacerlo, se rompería una abstracción. El código de llamada debería saber implícitamente que su BusinessService depende de DataService, que depende de la cadena de conexión.

Si tiene que hacerlo de manera absoluta, explíquelo. Eso es más o menos lo que está haciendo: resuelva el DataService con su dependencia de la cadena de conexión explícitamente, y resuelva explícitamente BusinessService pasando el DataService como dependencia.

Para hacer las cosas muy explícita (y más agradable de usar también) me gustaría sugerir el uso con tipo de fábrica para que en lugar de llamar directamente el contenedor

public interface IFactory 
{ 
    IDataService ResolveDataService(string connectionString); 
    IBussinessService ResolveBussinessService(IDataService dataService); 
    // possibly release method for IBussinessService as well 
} 
+0

ha revisado su enlace, pero en serio no puedo entender lo que dicen, ¿podemos decir o no tener una respuesta simple? –

+0

@moutasema My La pregunta simple es, cuando resuelvo el componente padre -pero- enviando parámetros para su hijo, ¿funcionaría eso? @moutasema ¿Puede el contenedor pasar automáticamente los parámetros al componente secundario? @kkozmic no, los argumentos en línea no son propagadas por la resolución de la tubería –

+2

@Moutasem al-awa: en pocas palabras, que estamos tratando de hacer algo que en el 99% de los casos es una mala práctica. Evite llamar directamente a 'Resolve()' (ya que está haciendo la ubicación del servicio y no la inyección de dependencia) y verá la forma correcta de hacerlo. –

1

he necesitado hacer esto cuando la creación de componentes transitorios que requiere un objeto de contexto. La solución que utilicé fue para anular la clase DefaultDependencyResolver para que pase los argumentos en línea a lo largo de la línea de resolución.

public class ArgumentPassingDependencyResolver : DefaultDependencyResolver 
{ 
    protected override CreationContext RebuildContextForParameter(
     CreationContext current, Type parameterType) 
    { 
     if (parameterType.ContainsGenericParameters) 
     { 
      // this behaviour copied from base class 
      return current; 
     } 

     // the difference in the following line is that "true" is passed 
     // instead of "false" as the third parameter 
     return new CreationContext(parameterType, current, true); 
    } 
} 

Una instancia de esta clase tiene que ser aprobada en cuando se crea el contenedor (otras clases también tienen que ser aprobada en porque no hay ningún constructor conveniente que sólo se necesita un solucionador de dependencias):

var container = new WindsorContainer(
    new DefaultKernel(
     new ArgumentPassingDependencyResolver(), 
     new NotSupportedProxyFactory()), 
    new DefaultComponentInstaller()); 
0

Sí, lo que solicita es posible, pero debería utilizar fábricas abstractas a través del Typed Factory Facility en lugar de solicitar su servicio directamente a través del contenedor.

Con las fábricas tipadas, todo lo que necesita hacer es definir las interfaces de fábrica y Windsor se encargará de la implementación por usted.

public interface IBusinessServiceFactory { 
    IBusinessService CreateBusinessService(string connString); 
} 

public interface IDataServiceFactory { 
    IDataService CreateDataService(string connString); 
} 

Se agrega la instalación y registrar su interfaz de fábricas de la siguiente manera:

container.AddFacility<TypedFactoryFacility>(); 
container.Register(Component.For<IDataServiceFactory>().AsFactory()); 
container.Register(Component.For<IBusinessServiceFactory>().AsFactory()); 

Su ahora se puede definir manualmente como su parámetro de tiempo de ejecución se pasa por el gráfico de objetos mediante la definición de un Dynamic Parameter en su registro BusinessService.

container.Register(Component.For<IBusinessService, BusinessService>() 
    .LifestyleTransient() 
    .DynamicParameters((k, d) => { 
     d["dataService"] = k.Resolve<IDataServiceFactory>.CreateDataService((string)d["connString"]); 
    })); 

Tenga en cuenta que las claves de diccionario tienen que coincidir con los nombres de los parámetros en el método y CreateBusinessServiceBusinessService constructor.

También debe hacerlo LifestyleTransient si tiene la intención de crear una nueva instancia cada vez que se llame al método de fábrica. (El valor predeterminado es singleton)

Cuestiones relacionadas