78

J'utilise l'unité de Microsoft pour l'injection de dépendance et je veux faire quelque chose comme ceci:Puis-je transmettre les paramètres du constructeur à la méthode Resolve() de Unity?

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

RepositoryA et RepositoryB ont tous deux un constructeur qui prend un paramètre IDataContext, et je veux l'unité pour initialiser le référentiel avec le contexte que je le passe. Notez également que IDataContext n'est pas enregistré avec Unity (je ne veux pas 3 instances de IDataContext).

Répondre

62

a ce jour, ils ont ajouté cette fonctionnalité:

Il est dans la dernière goutte ici:

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

Discussion sur le sujet ici:

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

Exemple:

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

Voir aussi http://stackoverflow.com/questions/2813322/unity-2-0-how-to-use -resolve-with-resolveroverride –

+5

le lien http://unity.codeplex.com/SourceControl/changeset/view/33899 n'est pas actif –

+2

"Class 'Microsoft.Practices.Unity.ParameterOverrides' n'a pas de paramètres de type". J'utilise Unity 3.5; ce code est-il valide uniquement pour une ancienne version de Unity? –

33

< 2 cents>

Que faire si vous plus tard décidez d'utiliser un autre service qui nécessite plus ou moins que le contexte? Le problème avec les paramètres constructeurs et IoC est que les paramètres sont finalement liés au type concret utilisé, par opposition à faire partie du contrat que l'interface de service définit. Ma suggestion serait que vous résolviez le contexte aussi bien, et je crois que l'unité devrait avoir un moyen pour vous d'éviter de construire 3 instances de celui-ci, ou vous devriez considérer un service d'usine qui a un moyen pour vous de construire L'object. Par exemple, que se passe-t-il si, par la suite, vous décidiez de construire un référentiel ne reposant pas du tout sur une base de données traditionnelle, mais d'utiliser un fichier XML pour produire des données fictives pour le test? Comment allez-vous nourrir le contenu XML vers ce constructeur?

IoC est basé sur le code de découplage, en liant le type et la sémantique des arguments aux types concrets, vous n'avez pas vraiment fait le découplage correctement, il y a toujours une dépendance.

"Ce code peut parler à n'importe quel type de référentiel, tant qu'il implémente cette interface .... Oh, et utilise un contexte de données". Maintenant, je sais que d'autres conteneurs IoC ont un support pour cela, et je l'ai eu dans ma première version aussi, mais à mon avis, ça ne fait pas partie de l'étape de la résolution.

</2 cents>

+3

Je vois votre point et d'accord avec vous, mais je dois encore les instances du RepositoryA et RepositoryB d'avoir le même IDataContext, qui doit être différent de celui RepositoryC. Notez également que IRepositoryA et IRepositoryB ont une propriété pour IDataContext. Je vais mettre à jour l'exemple de code un peu. – NotDan

+2

Bon point. J'étais sur le point d'ajouter un paramètre de chaîne au constructeur, mais après avoir vu ce point, j'ai décidé d'en faire un objet complet. Il se compose uniquement de la chaîne à ce stade, mais je peux déjà voir comment je pourrais ajouter des propriétés plus utiles –

0

NotDan, je pense que vous avez répondu à votre propre question dans les commentaires à lassevk. D'abord, j'utiliserais un LifetimeManager pour gérer le cycle de vie et le nombre d'instances d'IDataContext créées par Unity.
http://msdn.microsoft.com/en-us/library/cc440953.aspx

Il semble que l'objet ContainerControlledLifetimeManager vous donne la gestion d'instance dont vous avez besoin. Avec ce LifetimeManager en place, Unity doit ajouter la même instance de IDataContext à tous les objets qui nécessitent une dépendance IDataContext.

3

La réponse très courte est: non. Unity n'a actuellement aucun moyen de transmettre des paramètres dans le constructeur qui ne sont pas constants ou injectés, que j'ai pu trouver. À mon humble avis, c'est la plus grande chose qui lui manque, mais je pense que c'est par conception plutôt que par omission. Comme Jeff Fritz le note, vous pourriez en théorie créer un gestionnaire de durée de vie personnalisé qui sait quelle instance de contexte injecter dans différents types, mais c'est un niveau de codage en dur qui semble éviter le but d'utiliser Unity ou DI dans le première place.

Vous pouvez faire un petit pas en arrière par rapport à l'implémentation complète et rendre vos implémentations de référentiel responsables de l'établissement de leurs propres contextes de données. Le contexte instance peut encore être résolu à partir du conteneur, mais la logique pour décider lequel utiliser devra entrer dans l'implémentation du référentiel. Ce n'est pas aussi pur, certes, mais cela permettrait de se débarrasser du problème.

1

Une autre alternative, vous pouvez utiliser (ne sais pas vraiment si elle est une bonne pratique ou non) est la création de deux conteneurs et l'enregistrement d'une instance pour chaque:

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 

espoir que cela aide aussi

7

Vous pouvez utiliser InjectionConstructor/InjectionProperty/InjectionMethod selon votre architecture d'injection dans le ResolvedParameter < T> ("name") pour obtenir une instance d'un objet pré-enregistré dans le conteneur.

Dans votre cas, cet objet doit être enregistré avec un nom, et pour la même instance, vous avez besoin de ContainerControlledLifeTimeManager() en tant 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"))); 
+4

Êtes-vous sûr de ce code? Il ne compile pas ... 'Resolve' prend une collection de' ResolverOverride', et 'InjectionConstructor' n'est pas un' ResolverOverride'. –

+0

Yup Il semble mal. Bien que l'unité aurait dû le concevoir de cette façon. Si le nom du paramètre change toutes les pauses –

5

Merci les gars ... le mien est similaire à la poste par "Existe". Voir ci-dessous:

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

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