2009-10-02 12 views
12

Estoy usando Prism, que también ofrece el bonito contenedor Unity IoC. Soy nuevo en el concepto, por lo que todavía no he podido darle la vuelta. Lo que quiero hacer ahora es crear un objeto usando el contenedor IoC, pero pasando un parámetro extra también. Permítanme explicar con un ejemplo ..:Creando objetos usando Unity Resolve con parámetros adicionales

Tengo una clase que toma un objeto de comandos. Esto se ha registrado en el contenedor IoC, por lo que manejarlo muy bien:

public class Person 
{ 
    public Person(IApplicationCommands commands) { .. } 
    .. 
} 

Person person = _container.Resolve<Person>(); 

Ahora - Quiero pasar en otro argumento - por ejemplo, el nombre de la persona. Sin embargo, todavía quiero usar el contenedor IoC para manejar la resolución y, por lo tanto, obtener los otros parámetros del contenedor IoC. Pero pase el nombre como un parámetro "personalizado". Se puede hacer esto?

public class Person 
{ 
    public Person(IApplicationCommands commands, string name) { .. } 
    .. 
} 

string name = "John"; 
Person person = _container.Resolve<Person>(name); // ....?? 

Este ejemplo no parece funcionar, pero ¿hay alguna manera de hacerlo funcionar? ¿O el contenedor Unity IoC requiere que todos los parámetros se registren en el contenedor antes de llamar a Resolve?

Respuesta

9

Editar: Esta respuesta es obsoleta, en mi opinión, porque asume una versión anterior de Unity. NotDan's answer es mejor.


Tiene algunas opciones. Honestamente son un poco cojos, pero funcionarán.

Opción 1: Recipiente Scoped

Si desea utilizar la inyección de constructor , tendrá que crear un con ámbito de contenedores y poner sus datos en que con ámbito de contenedores:

IUnityContainer subContainer = _container.CreateChildContainer(); 

//Don't do this... create a custom type other than string, like 
// MyConstructorParams or something like that... this gets the point across. 
subContainer.RegisterInstance<string>("John"); 
Person person = subContainer.Resolve<Person>(); 

Opción 2: Método de inicialización

Lo que suelen hacer, sin embargo, es tener un método de inicialización separada en mis objetos de destino para las variables de instancia:

public class Person 
{ 
    public Person(IApplicationCommands commands) 
    { .. } 
    public void Initialize(string name) { .. } 

    .. 
} 

Y entonces su uso se convierte en:

Person person = container.Resolve<Person>(); 
person.Initialize("John"); 

Ninguno es particularmente agradable, pero hará el trabajo. Lo importante es escoger una convención y atenerse a ella, de lo contrario te perderás un poco.

Espero que esto ayude.

+0

No recomendaría cualquiera de estos enfoques. Si bien el uso de contenedores delimitados o el registro de instancias con nombres lógicos son ciertamente técnicas válidas para algunos escenarios, no son apropiados dado el contexto presunto de creación de nuevas entidades DDD. Ciertamente no podría predecir qué nuevos objetos de Persona podrían necesitar crearse si esto está relacionado con el nuevo registro de clientes, e incluso si pudiera ... bueno, creo que comprende el problema que se presentaría. La segunda opción es sin duda más viable, pero es mejor expresar las dependencias requeridas a través de los parámetros del constructor ... –

+0

Esto tampoco soluciona lo que veo es un defecto en el modelado de Person para empezar, y el uso directo del contenedor como un localizador de servicios para la creación de dicho tipo. –

+0

Ciertamente, la opción del contenedor de alcance es la más tonta de las dos opciones, pero funciona. También asumí que el OP simplificó su pregunta y no está tratando de hacer algo como esto en una Entidad simple, sino un objeto de servicio más complejo. Tengo este problema todo el tiempo con estos objetos donde el objeto tiene algunas dependencias, pero también necesito que se pasen algunos datos de instancia sobre los que tengo que actuar cuando se pasan. Esta es la razón por la que otros contenedores IoC, como Ninject, admiten exactamente lo que desea OP. La unidad aún no lo hace. –

0

No puedo pensar en ninguna forma de hacer esto con la inyección del constructor. Creo que necesitará usar property injection para las dependencias (marcado con el atributo Dependency) y luego tomar solo la cadena en el constructor, crear una instancia, luego usar BuildUp para conectar las dependencias, o hacer que la cadena sea una propiedad que usted también establecer manualmente después de Resolver.

4

Hay algunas opciones que podría considerar:

En el caso en que es necesario crear una nueva entidad que tiene legítimos dependencias, además de los datos que se suministra (por ejemplo, el nombre del cliente), encapsular esto en una fábrica que a su vez se ha inyectado en el objeto de llamar:

Person person = _personFactory.CreatePerson("bubba"); 

la fábrica puede ser inyectado con dependencias de la entidad y se suministra al constructor si se requiere o fijado por otros medios si opcional:

var person = new Person("bubba", _domainService); 

Para dependencias transitorios variables, como una estrategia utilizada por un método en particular, utilizar doble Despacho:

public class Person 
{ 
    public void DoSomethingWith(SomeStrategy strategy) 
    { 
     strategy.DoSomething(this); 
    } 
} 

+0

Esto solo es cierto si se está asumiendo que Person es solo una entidad simple, que es válida en este caso, pero no resuelve este inconveniente en Unity para todos los casos.Ninject y algunos otros contenedores IoC tienen la capacidad de hacer exactamente lo que el OP quiere, pero Unity no lo hace, dejándonos con algunas opciones bastante limitadas. Estoy de acuerdo en que el contenedor de alcance es la cosa más desagradable de la historia (no lo hago personalmente), pero es una solución. –

+0

La persona es solo una clase de ficción para simplificar esta pregunta. En mi caso, no hago esto para una clase de dominio, sino una representación de ViewModel de la clase de dominio. Estoy usando Prism, y la vista "Persona" en este caso tendrá que activar comandos para mostrar algunos subdatos en ciertas regiones, y para eso necesito los IApplicationCommands. – stiank81

+0

@Anderson Imes: No sabía que algunos contenedores lo permitieran. Me puedo imaginar algunos casos en los que esto podría ser útil, pero en general todavía recomiendo un enfoque de fábrica o de despacho doble sobre el uso de un contenedor como un localizador de servicios, ya que estos enfoques preservan la ignorancia de la infraestructura. Por lo menos, puede limitar la dependencia de su código a las invocaciones directas al contenedor al encapsular la técnica específica del contenedor en la fábrica. –

17

Can I pass constructor parameters to Unity's Resolve() method?

container.Resolve<IFoo>(new ParameterOverrides<Foo> { { "name", "bar" }, { "address", 42 } });" 
+2

¡Oye! ¡Muy agradable! No había visto esto pero es similar a lo que Ninject ha agregado recientemente. Tendré que desplegar la última versión, parece. ¡Gracias! –

+1

¿Qué ocurre si cambia el nombre del parámetro en el constructor? –

+3

Supongo que no workie .. – NotDan

Cuestiones relacionadas