Kann ich Konstruktorparameter an die Resolve () -Methode von Unity übergeben?


91

Ich verwende Microsoft Unity für die Abhängigkeitsinjektion und möchte Folgendes tun:

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

RepositoryAund RepositoryBbeide haben einen Konstruktor, der einen IDataContextParameter akzeptiert, und ich möchte, dass Unity das Repository mit dem Kontext initialisiert, den ich übergebe. Beachten Sie auch, dass dies IDataContextnicht bei Unity registriert ist (ich möchte keine 3 Instanzen von IDataContext).

Antworten:


71

Ab heute haben sie diese Funktionalität hinzugefügt:

Es ist im letzten Tropfen hier:

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

Diskussion hier:

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

Beispiel:

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



2
"Die Klasse 'Microsoft.Practices.Unity.ParameterOverrides' hat keine Typparameter". Ich verwende Unity 3.5. Ist dieser Code nur für eine ältere Version von Unity gültig?
Thomas Levesque

Für mich geht das. Hinweis: Ihre Klasse muss über einen parametrisierten Konstruktor mit den Parametern "name" und "address" verfügen. Foo(string name, int address) { ... }
Adun

Verwenden von Unity 2.1: container.Resolve<IFoo>(new ParameterOverrides { { "name", "bar" }, { "address", 42 } });
Mrfelis

38

<2 Cent>

Was ist, wenn Sie sich später für einen anderen Dienst entscheiden, der mehr oder weniger als nur den Kontext erfordert?

Das Problem mit Konstruktorparametern und IoC besteht darin, dass die Parameter letztendlich an den verwendeten konkreten Typ gebunden sind und nicht Teil des Vertrags sind, den die Serviceschnittstelle definiert.

Mein Vorschlag wäre, dass Sie entweder auch den Kontext auflösen, und ich glaube, Unity sollte eine Möglichkeit für Sie haben, das Erstellen von 3 Instanzen davon zu vermeiden, oder Sie sollten einen Factory-Service in Betracht ziehen, der eine Möglichkeit für Sie bietet, das Objekt zu erstellen.

Was ist zum Beispiel, wenn Sie sich später dazu entschließen, ein Repository zu erstellen, das überhaupt nicht auf einer herkömmlichen Datenbank basiert, sondern stattdessen eine XML-Datei verwendet, um Dummy-Daten für den Test zu erstellen? Wie würden Sie den XML-Inhalt diesem Konstruktor zuführen?

IoC basiert auf dem Entkopplungscode. Indem Sie den Typ und die Semantik der Argumente mit den konkreten Typen verknüpfen, haben Sie die Entkopplung wirklich nicht richtig durchgeführt. Es besteht immer noch eine Abhängigkeit.

"Dieser Code kann möglicherweise mit jeder Art von Repository kommunizieren, solange er diese Schnittstelle implementiert ... Oh, und verwendet einen Datenkontext."

Jetzt weiß ich, dass andere IoC-Container dies unterstützen, und ich hatte es auch in meiner ersten eigenen Version, aber meiner Meinung nach gehört es nicht zum Auflösungsschritt.

</ 2 Cent>


3
Ich verstehe Ihren Standpunkt und stimme Ihnen zu, aber ich benötige weiterhin die Instanzen von RepositoryA und RepositoryB, um denselben IDataContext zu haben, der sich von RepositoryC unterscheiden muss. Beachten Sie auch, dass IRepositoryA und IRepositoryB eine Eigenschaft für IDataContext haben. Ich werde den Beispielcode ein wenig aktualisieren.
NotDan

2
Toller Punkt. Ich wollte dem Konstruktor einen String-Parameter hinzufügen, aber nachdem ich diesen Punkt betrachtet hatte, entschied ich mich, ihn zu einem vollständigen Objekt zu machen. Es besteht zu diesem Zeitpunkt nur aus der Zeichenfolge, aber ich kann bereits sehen, wie ich weitere nützliche Eigenschaften hinzufügen kann
Santosh Benjamin

9

Danke Jungs ... meins ähnelt dem Beitrag von "Exist". Siehe unten:

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

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

5

Sie können InjectionConstructor / InjectionProperty / InjectionMethod abhängig von Ihrer Injection-Architektur im ResolvedParameter <T> ("Name") verwenden, um eine Instanz eines vorregistrierten Objekts im Container abzurufen.

In Ihrem Fall muss dieses Objekt mit einem Namen registriert sein, und für dieselbe Versicherung benötigen Sie ContainerControlledLifeTimeManager () wie den 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
Sind Sie sicher über diesen Code? Es wird nicht kompiliert ... Resolvenimmt eine Sammlung von ResolverOverrideund InjectionConstructorist keine ResolverOverride.
Thomas Levesque

Ja, es sieht falsch aus. Obwohl die Einheit es so hätte gestalten sollen. Wenn sich der Parametername ändert, bricht alles zusammen
Frank Q.

3

Die sehr kurze Antwort lautet: nein. Unity hat derzeit keine Möglichkeit, Parameter an den Konstruktor zu übergeben, die nicht konstant oder injiziert sind und die ich finden konnte. IMHO ist das die größte Sache, die es fehlt, aber ich denke, es ist eher beabsichtigt als ausgelassen.

Wie Jeff Fritz bemerkt, könnten Sie theoretisch einen benutzerdefinierten Lifetime-Manager erstellen, der weiß, welche Kontextinstanz in verschiedene Typen eingefügt werden soll. Dies ist jedoch eine Ebene der Hardcodierung, die den Zweck der Verwendung von Unity oder DI in erster Linie zu umgehen scheint.

Sie können einen kleinen Schritt von der vollständigen DI zurücktreten und Ihre Repository-Implementierungen für die Einrichtung ihrer eigenen Datenkontexte verantwortlich machen. Die Kontext - Instanz kann immer noch aus dem Behälter gelöst werden , aber die Logik für die Entscheidung , die man verwenden, um in die Umsetzung des Endlagers gehen würde. Es ist sicherlich nicht so rein, aber es würde das Problem beseitigen.


1

Eine andere Alternative, die Sie verwenden könnten (ich weiß nicht wirklich, ob es sich um eine bewährte Methode handelt oder nicht), besteht darin, zwei Container zu erstellen und für jeden eine Instanz zu registrieren:

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

hoffe das hilft auch


0

NotDan, ich denke, Sie haben möglicherweise Ihre eigene Frage in Kommentaren an lassevk beantwortet.

Zunächst würde ich einen LifetimeManager verwenden, um den Lebenszyklus und die Anzahl der von Unity erstellten IDataContext-Instanzen zu verwalten.
http://msdn.microsoft.com/en-us/library/cc440953.aspx

Es hört sich so an ContainerControlledLifetimeManager, als würde Ihnen das Objekt die Instanzverwaltung geben, die Sie benötigen. Mit diesem LifetimeManager sollte Unity allen Objekten, für die eine IDataContext-Abhängigkeit erforderlich ist, dieselbe Instanz des IDataContext hinzufügen.

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.