Posso passar parâmetros do construtor para o método Resolve () do Unity?

91

Estou usando o Unity da Microsoft para injeção de dependência e quero fazer algo assim:

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);

RepositoryAe RepositoryBambos têm um construtor que leva um IDataContextparâmetro, e eu quero que o Unity inicialize o repositório com o contexto que passei. Observe também que IDataContextnão está registrado no Unity (não quero 3 instâncias de IDataContext).

NotDan
fonte

Respostas:

71

A partir de hoje, eles adicionaram esta funcionalidade:

Está na última versão aqui:

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

Discussão sobre isso aqui:

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

Exemplo:

container.Resolve<IFoo>(new ParameterOverrides<Foo> { { "name", "bar" }, { "address", 42 } });"
Existir
fonte
Consulte também stackoverflow.com/questions/2813322/…
Michael Freidgeim
5
o link unit.codeplex.com/SourceControl/changeset/view/33899 não está ativo
M.Kumaran
2
"A classe 'Microsoft.Practices.Unity.ParameterOverrides' não possui parâmetros de tipo". Estou usando o Unity 3.5; este código é válido apenas para uma versão mais antiga do Unity?
Thomas Levesque
Funciona para mim. Nota: Sua classe deve ter um construtor parametrizado com o parâmetro "nome" e o parâmetro "endereço". Foo(string name, int address) { ... }
Adun
Usando Unity 2.1: container.Resolve<IFoo>(new ParameterOverrides { { "name", "bar" }, { "address", 42 } });
mrfelis
38

<2 centavos>

E se mais tarde você decidir usar um serviço diferente que requer mais ou menos do que apenas o contexto?

O problema com os parâmetros do construtor e IoC é que os parâmetros estão, em última análise, vinculados ao tipo concreto que está sendo usado, em vez de fazer parte do contrato que a interface de serviço define.

Minha sugestão seria que você resolvesse o contexto também, e eu acredito que o Unity deve ter uma maneira de evitar a construção de 3 instâncias dele, ou você deve considerar um serviço de fábrica que tem uma maneira para você construir o objeto.

Por exemplo, e se você mais tarde decidir construir um repositório que não dependa de um banco de dados tradicional, mas em vez disso use um arquivo XML para produzir dados fictícios para o teste? Como você alimentaria o conteúdo XML para esse construtor?

IoC é baseado em código de desacoplamento, amarrando o tipo e semântica dos argumentos aos tipos concretos, você realmente não fez o desacoplamento corretamente, ainda há uma dependência.

"Este código pode se comunicar com qualquer tipo de repositório, possivelmente, desde que implemente essa interface ... Ah, e use um contexto de dados".

Agora, eu sei que outros contêineres IoC têm suporte para isso, e eu o tinha em minha primeira versão também, mas na minha opinião, isso não pertence à etapa de resolução.

</ 2 centavos>

Lasse V. Karlsen
fonte
3
Entendo seu ponto e concordo com você, no entanto, ainda preciso que as instâncias de RepositoryA e RepositoryB tenham o mesmo IDataContext, que precisa ser diferente de RepositoryC. Observe também que IRepositoryA e IRepositoryB tem uma propriedade para IDataContext. Vou atualizar um pouco o código de amostra.
NotDan
2
Ótimo ponto. Eu estava prestes a adicionar um parâmetro de string ao construtor, mas depois de ver este ponto, decidi torná-lo um objeto completo. Ele consiste apenas na string neste ponto, mas eu já posso ver como poderia adicionar propriedades mais úteis a ela
Santosh Benjamin
9

Obrigado galera ... o meu é parecido com o post do "Exist". Ver abaixo:

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

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

Você pode usar InjectionConstructor / InjectionProperty / InjectionMethod dependendo da sua arquitetura de injeção em ResolvedParameter <T> ("nome") para obter uma instância de um objeto pré-registrado no contêiner.

No seu caso, este objeto deve ser registrado com um nome, e para o mesmo caso, você precisa de ContainerControlledLifeTimeManager () como o 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
fonte
4
Tem certeza sobre este código? Ele não compila ... Resolvepega uma coleção de ResolverOverridee InjectionConstructornão é um ResolverOverride.
Thomas Levesque
Sim, parece errado. Embora a unidade devesse ter projetado dessa forma. Se o nome do parâmetro mudar, tudo será interrompido
Frank Q.
3

A resposta muito curta é: não. Atualmente, o Unity não tem como passar parâmetros para o construtor que não sejam constantes ou injetados, que eu consegui encontrar. IMHO, essa é a maior coisa que está faltando, mas acho que é por design, e não por omissão.

Como observa Jeff Fritz, você poderia, em teoria, criar um gerenciador de tempo de vida customizado que sabe qual instância de contexto injetar em vários tipos, mas esse é um nível de codificação que parece evitar o propósito de usar Unity ou DI em primeiro lugar.

Você poderia dar um pequeno passo para trás em relação ao DI completo e tornar as implementações de seu repositório responsáveis ​​por estabelecer seus próprios contextos de dados. A instância de contexto ainda pode ser resolvida a partir do contêiner, mas a lógica para decidir qual usar teria que ir para a implementação do repositório. Certamente não é tão puro, mas acabaria com o problema.

Neil Hewitt
fonte
1

Outra alternativa que você pode usar (não sei se é uma boa prática ou não) é criar dois contêineres e registrar uma instância para cada um:

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 isso ajude também

Samuel Carrijo
fonte
0

NotDan, acho que você pode ter respondido sua própria pergunta em comentários para lassevk.

Primeiro, eu usaria um LifetimeManager para gerenciar o ciclo de vida e o número de instâncias de IDataContext que o Unity cria.
http://msdn.microsoft.com/en-us/library/cc440953.aspx

Parece que o ContainerControlledLifetimeManagerobjeto fornecerá o gerenciamento de instância de que você precisa. Com esse LifetimeManager em vigor, o Unity deve adicionar a mesma instância do IDataContext a todos os objetos que requerem uma dependência IDataContext.

Jeff Fritz
fonte