2016-05-17 13 views
0

Ich habe eine Schnittstelle, die mehrere Implementierungen hat. Diese Implementierungen werden über Namen Bindungen aufgebaut:Wie verzieren benannte Bindungen in Ninject

Bind<IService>().To<FirstService>().Named("First"); 
Bind<IService>().To<SecondService>().Named("Second"); 
Bind<IService>().To<ThirdService>().Named("Third"); 

jedoch jeder dieser Dienste eingerichtet werden müssen, auch:

Bind<IService>().To<FirstService>().WhenInjectedInto<FirstDecorator>(); 
Bind<IService>().To<FirstDecorator>().WhenInjectedInto<SecondDecorator>(); 
Bind<IService>().To<SecondDecorator>(); 

Ich weiß, dass es die WhenParentNamed und WhenAnyAncestorNamed kontextuellen Bindung, aber sie scheinen sich mit der WhenInjectedInto gegenseitig zu schließen. Wie kann ich Ninject so einrichten, dass sowohl die Dekoration als auch die benannten Bindungen berücksichtigt werden? Ich habe ein Provider Schema eingerichtet, das funktioniert, aber ich wollte sehen, ob es eine Möglichkeit gab, dies nativ in Ninject zu tun, ohne die benutzerdefinierten Anbieter.

Bind<IService>().ToProvider(new ServiceProvider("First")).Named("First"); 
Bind<IService>().ToProvider(new ServiceProvider("Second")).Named("Second"); 
Bind<IService>().ToProvider(new ServiceProvider("Third")).Named("Third"); 

class ServiceProvider : Provider<IService> 
{ 
    private readonly string name; 

    public ServiceProvider(string name) 
    { 
     this.name = name; 
    } 

    protected override CreateInstance(IContext context) 
    { 
     var service = GetService(name); 

     var otherDependency = context.Kernel.Get<OtherDependency>(); 
     service = new FirstDecorator(service, otherDependency); 

     service = new SecondDecorator(service); 

     return service; 
    } 

    private IService GetService(string name, IContext context) 
    { 
     switch(name) 
     { 
      case "First": return context.Kernel.Get<FirstService>(); 
      case "Second": return context.Kernel.Get<SecondService>(); 
      case "Third": return context.Kernel.Get<ThirdService>(); 
      default: throw new ArugmentException($"No binding for {name}"); 
     } 
    } 
} 

Ich habe bei this gesucht, aber da sie nicht mit dem Namen Bindungen beschäftigen, ist es nicht anwendbar zu sein scheint.

Idealerweise wäre es so etwas wie dieses:

Bind<IService>().To<FirstService>().WhenInjectedInto<FirstDecorator>().Named("First"); 
Bind<IService>().To<SecondService>().WhenInjectedInto<FirstDecorator>().Named("Second"); 
Bind<IService>().To<ThirdService>().WhenInjectedInto<FirstDecorator>().Named("Third"); 
Bind<IService>().To<FirstDecorator>().WhenInjectedInto<SecondDecorator>(); 
Bind<IService>().To<SecondDecorator>(); 

aktualisieren: Hier ist, wie ich für eine dieser dekoriert Dienste aufrufen erwarten:

kernel.Get<IService>("First"); 

oder

[Inject, Named("First")] 
public IService Service {get; set;} 

Die letzte ist für einige automatisierte Integrationstests.

Update 2: Ich habe dies versucht, und es funktioniert nicht (klagt über doppelte Bindungen, vermutlich, weil es nicht die Namen auf den konkreten Umsetzung Bindungen zu ehren)

Bind<IService>().To<FirstService>().WhenInjectedInto<FirstDecorator>().Named("First"); 
Bind<IService>().To<SecondService>().WhenInjectedInto<FirstDecorator>().Named("Second"); 
Bind<IService>().To<ThirdService>().WhenInjectedInto<FirstDecorator>().Named("Third"); 
Bind<IService>().To<FirstDecorator>().WhenInjectedInto<SecondDecorator>().Named("First"); 
Bind<IService>().To<FirstDecorator>().WhenInjectedInto<SecondDecorator>().Named("Second"); 
Bind<IService>().To<FirstDecorator>().WhenInjectedInto<SecondDecorator>().Named("Third"); 
Bind<IService>().To<SecondDecorator>().Named("First"); 
Bind<IService>().To<SecondDecorator>().Named("Second"); 
Bind<IService>().To<SecondDecorator>().Named("Third"); 

Update 3: sie folgendermaßen vorgehen NAHEZU funktioniert:

Bind<IService>().To<FirstService>().WhenAnyAncestorNamed("First"); 
Bind<IService>().To<SecondService>().WhenAnyAncestorNamed("Second"); 
Bind<IService>().To<ThirdService>().WhenAnyAncestorNamed("Third"); 

Bind<IService>().To<FirstDecorator>().WhenInjectedInto<SecondDecorator>().Named("First"); 

Bind(typeof(IService)).To(typeof(SecondDecorator)).Named("First"); 
Bind(typeof(IService)).To(typeof(SecondDecorator)).Named("Second"); 
Bind(typeof(IService)).To(typeof(SecondDecorator)).Named("Third"); 

das Problem ist, dass, wenn es die SecondDecorator zu bauen versucht, gibt es zwei passende Bindungen: eine zur konkreten Implementierung (wenn sie "First" genannt wird, dann FirstService), und eine zu der FirstDecorator.

+0

Wie erwarten Sie den Service zu lösen? Durch Aufruf von 'Get ("First") '? –

+0

Korrigieren. Ich werde die Frage mit diesen Informationen aktualisieren – cidthecoatrack

Antwort

0

Ich landete Hand rollen meine eigene Dekoration Methode:

public override void Load() 
{ 
    var decorators = new[] 
    { 
     typeof(FirstDecorator), 
     typeof(SecondDecorator) 
    }; 

    Decorate<IService, FirstService>("First", decorators); 
    Decorate<IService, SecondService>("Second", decorators); 
    Decorate<IService, ThirdService>("Third", decorators); 
} 

private void Decorate<S, T>(string name, params Type[] decorators) 
    where T : S 
{ 
    var allImplementations = new[] { typeof(T) }.Union(decorators); 

    foreach (var implementation in allImplementations) 
    { 
     Bind<S>().To(implementation).When(r => Need(implementation, r, name, allImplementations)); 
    } 

    Bind<S>().To(allImplementations.Last()).Named(name); 
} 

private bool Need(Type implementation, IRequest request, string name, IEnumerable<Type> implementations) 
{ 
    var implementationsList = implementations.ToList(); 
    var depth = implementationsList.Count - implementationsList.IndexOf(implementation); 

    return request.Depth == depth && request.ActiveBindings.Any(b => b.Metadata.Name == name); 
} 

Ich werde schauen, um zu sehen, ob ich diese in eine When -Typs Klausel Refactoring kann und legt als Pull-Anforderung an Ninject