¿Puedo pasar parámetros de constructor al método Resolve () de Unity?

91

Estoy usando Unity de Microsoft para la inyección de dependencia y quiero hacer algo como esto:

IDataContext context = _unityContainer.Resolve<IDataContext>();
var repositoryA = _unityContainer.Resolve<IRepositoryA>(context); //Same instance of context
var repositoryB = _unityContainer.Resolve<IRepositoryB>(context); //Same instance of context

IDataContext context2 = _unityContainer.Resolve<IDataContext>(); //New instance
var repositoryA2 = _unityContainer.Resolve<IRepositoryA>(context2);

RepositoryAy RepositoryBambos tienen un constructor que toma un IDataContextparámetro, y quiero que Unity inicialice el repositorio con el contexto que le paso. También tenga en cuenta que IDataContextno está registrado con Unity (no quiero 3 instancias de IDataContext).

NotDan
fuente

Respuestas:

71

A día de hoy han agregado esta funcionalidad:

Está en la última gota aquí:

http://unity.codeplex.com/SourceControl/changeset/view/33899

Discusión sobre esto aquí:

http://unity.codeplex.com/Thread/View.aspx?ThreadId=66434

Ejemplo:

container.Resolve<IFoo>(new ParameterOverrides<Foo> { { "name", "bar" }, { "address", 42 } });"
Existe
fuente
Véase también stackoverflow.com/questions/2813322/…
Michael Freidgeim
5
el enlace unity.codeplex.com/SourceControl/changeset/view/33899 no está activo
M.Kumaran
2
"La clase 'Microsoft.Practices.Unity.ParameterOverrides' no tiene parámetros de tipo". Estoy usando Unity 3.5; ¿Este código es válido solo para una versión anterior de Unity?
Thomas Levesque
Esto funciona para mi. Nota: Su clase debe tener un constructor parametrizado con el parámetro "nombre" y el parámetro "dirección". Foo(string name, int address) { ... }
Adun
Usando Unity 2.1: container.Resolve<IFoo>(new ParameterOverrides { { "name", "bar" }, { "address", 42 } });
mrfelis
38

<2 centavos>

¿Qué pasa si más adelante decide utilizar un servicio diferente que requiere más o menos que solo el contexto?

El problema con los parámetros del constructor y el IoC es que, en última instancia, los parámetros están vinculados al tipo concreto que se utiliza, en lugar de formar parte del contrato que define la interfaz de servicio.

Mi sugerencia sería que resuelva el contexto también, y creo que Unity debería tener una forma de evitar la construcción de 3 instancias de él, o debería considerar un servicio de fábrica que tenga una forma de construir el objeto.

Por ejemplo, ¿qué pasa si más adelante decide construir un repositorio que no dependa en absoluto de una base de datos tradicional, sino que utilice un archivo XML para producir datos ficticios para la prueba? ¿Cómo haría para alimentar el contenido XML a ese constructor?

IoC se basa en el código de desacoplamiento, al vincular el tipo y la semántica de los argumentos a los tipos concretos, realmente no ha realizado el desacoplamiento correctamente, todavía hay una dependencia.

"Este código puede comunicarse con cualquier tipo de repositorio posiblemente, siempre que implemente esta interfaz ... Ah, y use un contexto de datos".

Ahora, sé que otros contenedores de IoC tienen soporte para esto, y también lo tenía en mi primera versión, pero en mi opinión, no pertenece al paso de resolución.

</ 2 centavos>

Lasse V. Karlsen
fuente
3
Veo su punto y estoy de acuerdo con usted, sin embargo, todavía necesito que las instancias de RepositoryA y RepositoryB tengan el mismo IDataContext, que debe ser diferente de RepositoryC. También tenga en cuenta que IRepositoryA e IRepositoryB tiene una propiedad para IDataContext. Actualizaré un poco el código de muestra.
NotDan
2
Gran punto. Estaba a punto de agregar un parámetro de cadena al constructor, pero después de ver este punto, decidí convertirlo en un objeto completo. En este punto, solo consta de la cadena, pero ya puedo ver cómo podría agregarle propiedades más útiles
Santosh Benjamin
9

Gracias chicos ... el mío es similar al post de "Exist". Vea abajo:

        IUnityContainer container = new UnityContainer();
        container.LoadConfiguration();

        _activeDirectoryService = container.Resolve<IActiveDirectoryService>(new ResolverOverride[]
        {
            new ParameterOverride("activeDirectoryServer", "xyz.adserver.com")
        });
Kwex
fuente
5

Puede usar InjectionConstructor / InjectionProperty / InjectionMethod según su Arquitectura de inyección dentro del ResolvedParameter <T> ("nombre") para obtener una instancia de un Objeto registrado previamente en el contenedor.

En su caso, este Objeto debe estar registrado con un Nombre, y para el mismo ingreso necesita ContainerControlledLifeTimeManager () que LifeTimeManager.

_unityContainer.RegisterType<IDataContext,DataContextA>("DataContextA", new ContainerControlledLifeTimeManager());
_unityContainer.RegisterType<IDataContext,DataContextB>("DataContextB");

  var repositoryA = _unityContainer.Resolve<IRepositoryA>(new InjectionConstructor(
new ResolvedParameter<IDataContext>("DataContextA")));

  var repositoryB = _unityContainer.Resolve<IRepositoryB>(new InjectionConstructor(
new ResolvedParameter<IDataContext>("DataContextA")));

  var repositoryA2 = _unityContainer.Resolve<IRepositoryA>(new InjectionConstructor(
new ResolvedParameter<IDataContext>("DataContextB")));
Trecenti
fuente
4
¿Estás seguro de este código? No se compila ... Resolvetoma una colección de ResolverOverridey InjectionConstructorno es un ResolverOverride.
Thomas Levesque
Sí, se ve mal. Aunque la unidad debería haberlo diseñado de esa manera. Si el nombre del parámetro cambia, todo se rompe
Frank Q.3 de
3

La respuesta muy corta es: no. Actualmente, Unity no tiene forma de pasar parámetros al constructor que no sean constantes o inyectados, que he podido encontrar. En mi humilde opinión, eso es lo más importante que le falta, pero creo que es por diseño y no por omisión.

Como señala Jeff Fritz, en teoría podría crear un administrador de por vida personalizado que sepa qué instancia de contexto inyectar en varios tipos, pero ese es un nivel de codificación que parece obviar el propósito de usar Unity o DI en primer lugar.

Podría dar un pequeño paso atrás de la DI completa y hacer que las implementaciones de su repositorio sean responsables de establecer sus propios contextos de datos. La instancia de contexto aún se puede resolver desde el contenedor, pero la lógica para decidir cuál usar tendría que ir a la implementación del repositorio. No es tan puro, ciertamente, pero eliminaría el problema.

Neil Hewitt
fuente
1

Otra alternativa que podría utilizar (realmente no sé si es una buena práctica o no) es crear dos contenedores y registrar una instancia para cada uno:

IDataContext context = _unityContainer.Resolve<IDataContext>();
_unityContainer.RegisterInstance(context);
var repositoryA = _unityContainer.Resolve<IRepositoryA>(); //Same instance of context
var repositoryB = _unityContainer.Resolve<IRepositoryB>(); //Same instance of context


//declare _unityContainer2
IDataContext context2 = _unityContainer2.Resolve<IDataContext>(); //New instance
_unityContainer2.RegisterInstance(context2);
var repositoryA2 = _unityContainer2.Resolve<IRepositoryA>(context2); //will retrieve the other instance

espero que esto también ayude

Samuel Carrijo
fuente
0

NotDan, creo que puede haber respondido a su propia pregunta en los comentarios a lassevk.

Primero, usaría LifetimeManager para administrar el ciclo de vida y la cantidad de instancias de IDataContext que crea Unity.
http://msdn.microsoft.com/en-us/library/cc440953.aspx

Parece que el ContainerControlledLifetimeManagerobjeto le brindará la administración de instancias que necesita. Con ese LifetimeManager en su lugar, Unity debería agregar la misma instancia de IDataContext a todos los objetos que requieren una dependencia de IDataContext.

Jeff Fritz
fuente