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

NotDan
quelle

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 } });"
Existieren
quelle
Siehe auch stackoverflow.com/questions/2813322/…
Michael Freidgeim
5
Der Link unit.codeplex.com/SourceControl/changeset/view/33899 ist nicht aktiv
M.Kumaran
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>

Lasse V. Karlsen
quelle
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")
        });
Kwex
quelle
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")));
Trecenti
quelle
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.

Neil Hewitt
quelle
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

Samuel Carrijo
quelle
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.

Jeff Fritz
quelle