web-dev-qa-db-ger.com

Wie füge ich mit Unity eine benannte Abhängigkeit in einen Konstruktor ein?

Ich habe IRespository zweimal (mit Namen) im folgenden Code registriert:

// Setup the Client Repository
IOC.Container.RegisterType<ClientEntities>(new InjectionConstructor());
IOC.Container.RegisterType<IRepository, GenericRepository>
    ("Client", new InjectionConstructor(typeof(ClientEntities)));

// Setup the Customer Repository
IOC.Container.RegisterType<CustomerEntities>(new InjectionConstructor());
IOC.Container.RegisterType<IRepository, GenericRepository>
    ("Customer", new InjectionConstructor(typeof(CustomerEntities)));

IOC.Container.RegisterType<IClientModel, ClientModel>();
IOC.Container.RegisterType<ICustomerModel, CustomerModel>();

Aber wenn ich dieses Problem lösen möchte (um IRepository zu verwenden), muss ich das Problem manuell beheben:

public ClientModel(IUnityContainer container)
{
   this.dataAccess = container.Resolve<IRepository>(Client);

   .....
}

Was ich möchte, ist, dass es im Konstruktor aufgelöst wird (genau wie IUnityContainer). Ich brauche einen Weg, um zu sagen, welcher benannte Typ aufgelöst werden soll.

So etwas wie: (HINWEIS: Nicht realer Code)

public ClientModel([NamedDependancy("Client")] IRepository dataAccess)
{
   this.dataAccess = dataAccess;

   .....
}

Gibt es eine Möglichkeit, meinen gefälschten Code zum Laufen zu bringen?

64
Vaccano

Sie können Abhängigkeiten mit oder ohne Namen in der API, den Attributen oder über die Konfigurationsdatei konfigurieren. Sie haben oben nicht XML erwähnt, also gehe ich davon aus, dass Sie die API verwenden.

Um dem Container mitzuteilen, dass er eine benannte Abhängigkeit auflösen soll, müssen Sie ein InjectionParameter-Objekt verwenden. Führen Sie für Ihr ClientModel-Beispiel Folgendes aus:

container.RegisterType<IClientModel, ClientModel>(
    new InjectionConstructor(                        // Explicitly specify a constructor
        new ResolvedParameter<IRepository>("Client") // Resolve parameter of type IRepository using name "Client"
    )
);

Dies teilt dem Container mit: "Rufen Sie beim Auflösen von ClientModel den Konstruktor auf, der einen einzelnen IRepository-Parameter verwendet. Wenn Sie diesen Parameter auflösen, lösen Sie zusätzlich zum Typ den Namen 'Client' auf."

Wenn Sie Attribute verwenden wollten, funktioniert Ihr Beispiel fast, Sie müssen nur den Attributnamen ändern:

public ClientModel([Dependency("Client")] IRepository dataAccess)
{
   this.dataAccess = dataAccess;

   .....
}
75
Chris Tavares

Dies ist eine sehr späte Antwort, aber die Frage wird immer noch in Google angezeigt.

So und so, 5 Jahre später ...

Ich habe einen ziemlich einfachen Ansatz. Normalerweise, wenn Sie "Named Dependency" verwenden müssen, liegt dies daran, dass Sie versuchen, eine Art Strategiemuster zu implementieren. In diesem Fall lege ich einfach eine Indirektionsebene zwischen Unity und dem Rest meines Codes an, die als StrategyResolver bezeichnet wird, um nicht direkt von Unity abhängig zu sein.

public class StrategyResolver : IStrategyResolver
{
    private IUnityContainer container;

    public StrategyResolver(IUnityContainer unityContainer)
    {
        this.container = unityContainer;
    }

    public T Resolve<T>(string namedStrategy)
    {
        return this.container.Resolve<T>(namedStrategy);
    }
}

Verwendungszweck:

public class SomeClass: ISomeInterface
{
    private IStrategyResolver strategyResolver;

    public SomeClass(IStrategyResolver stratResolver)
    {
        this.strategyResolver = stratResolver;
    }

    public void Process(SomeDto dto)
    {
        IActionHandler actionHanlder = this.strategyResolver.Resolve<IActionHandler>(dto.SomeProperty);
        actionHanlder.Handle(dto);
    }
}

Anmeldung:

container.RegisterType<IActionHandler, ActionOne>("One");
container.RegisterType<IActionHandler, ActionTwo>("Two");
container.RegisterType<IStrategyResolver, StrategyResolver>();
container.RegisterType<ISomeInterface, SomeClass>();

Das Schöne daran ist, dass ich den StrategyResolver nie mehr ansprechen muss, wenn ich in Zukunft neue Strategien hinzufüge.

Es ist sehr einfach. Sehr sauber und ich habe die Abhängigkeit von Unity auf ein Minimum reduziert. Das einzige Mal, wenn ich den StrategyResolver berühren würde, ist, wenn ich mich dazu entscheide, die Containertechnologie zu ändern, was sehr unwahrscheinlich ist.

Hoffe das hilft!

Edit: Ich mag die akzeptierte Antwort nicht wirklich, denn wenn Sie das Dependency-Attribut im Konstruktor Ihres Service verwenden, haben Sie tatsächlich eine starke Abhängigkeit von Unity. Das Dependency-Attribut ist Teil der Unity-Bibliothek. An diesem Punkt könnten Sie auch überall eine IUnityContainer-Abhängigkeit übergeben.

Ich ziehe es vor, dass meine Service-Klassen von Objekten abhängen, die ich vollständig besitze, anstatt eine harte Abhängigkeit von einer externen Bibliothek überall zu haben. Durch die Verwendung des Attributs Dependency werden die Signaturen der Konstruktoren weniger sauber und einfach.

Darüber hinaus ermöglicht diese Technik das Auflösen benannter Abhängigkeiten zur Laufzeit, ohne dass die benannten Abhängigkeiten im Konstruktor, in der Anwendungskonfigurationsdatei fest codiert werden müssen oder InjectionParameter verwendet werden. Dies sind alle Methoden, die wissen müssen, welche benannten Abhängigkeiten zur Entwurfszeit zu verwenden sind.

Bearbeiten (2016-09-19): Für diejenigen, die sich fragen, kann der Container sich selbst übergeben, wenn Sie IUnityContainer als Abhängigkeit anfragen, wie in der Konstruktorsignatur StrategyResolver gezeigt.

Bearbeiten (2018-10-20): Hier eine andere Möglichkeit, einfach eine Fabrik zu verwenden:

public class SomeStrategyFactory : ISomeStrategyFactory
{
    private IStrategy _stratA;
    private IStrategy _stratB;

    public SomeFactory(IStrategyA stratA, IStrategyB stratB)
    {
        _stratA = stratA;
        _stratB = stratB;
    }

    public IStrategy GetStrategy(string namedStrategy){
        if (namedStrategy == "A") return _stratA;
        if (namedStrategy == "B") return _stratB;
    }
}

public interface IStrategy {
    void Execute();
}

public interface IStrategyA : IStrategy {}

public interface IStrategyB : IStrategy {}

public class StrategyA : IStrategyA {
    public void Execute(){}
}

public class StrategyB : IStrategyB {
    public void Execute() {}
}

Verwendungszweck:

public class SomeClass : ISomeClass
{
    public SomeClass(ISomeStrategyFactory strategyFactory){

        IStrategy strat = strategyFactory.GetStrategy("HelloStrategy");
        strat.Execute();

    }
}

Anmeldung:

container.RegisterType<ISomeStrategyFactory, SomeStrategyFactory>();
container.RegisterType<IStrategyA, StrategyA>();
container.RegisterType<IStrategyB, StrategyB>();
container.RegisterType<ISomeClass, SomeClass>();

Dieser zweite Vorschlag ist dasselbe, jedoch unter Verwendung des Fabrikdesignmusters.

Hoffe das hilft!

22
TchiYuan

Sie sollten ParameterOverrides verwenden können

var repository = IOC.Container.Resolve<IRepository>("Client");
var clientModel = IOC.Container.Resolve<ClientModel>(new ParameterOverrides<ClientModel> { {"dataAccess", repository } } );

edit: .__ Ich bin mir nicht sicher, warum Sie den UnityContainer umgehen - persönlich fügen wir unsere Abhängigkeiten in den Konstruktor selbst ein (was "normal" von dem ist, was ich gesehen habe). Sie können jedoch in Ihren RegisterType- und Resolve-Methoden einen Namen angeben.

IOC.Container.RegisterType<IRepository, GenericRepository>("Client");
IOC.Container.Resolve<IRepository>("Client");

und Sie erhalten den Typ, den Sie für diesen Namen registriert haben.

3
Kyle W

Tun Sie dies nicht - erstellen Sie einfach einen class ClientRepository : GenericRepository { } und verwenden Sie das Typsystem.

0
mcintyre321