2012-12-26 2 views
13

Angenommen, ich habe die folgenden Klassen, die ich mit Ninject konstruieren möchte, mit den Pfeilen, die Abhängigkeiten zeigen.Ninject Scoping - verwenden Sie die gleiche Instanz über den gesamten zu erstellenden Graph

A > B > D 
A > C > D 

Ich möchte Ninject so konfigurieren, dass eine vorübergehende scoped ist, das heißt jedes Mal, wenn Ninject fragen Sie nach einem A, erhalten Sie ein neues. Ich möchte auch, dass B und C vorübergehend sind, du bekommst jedes Mal ein neues, wenn du nach einem A fragst. Aber ich möchte, dass das D über B und C wiederverwendet wird. Also jedes Mal, wenn ich ein A anfordere will, dass Ninject eines von jedem Objekt konstruiert, nicht zwei Ds. Aber ich möchte nicht, dass DS über verschiedene As wiederverwendet werden.

Was ist der beste Weg, um dies mit Ninject einzurichten?

Update:
Nach einigen mehr Forschung, scheint es wie Unity eine PerResolveLifetimeManager hat, die das tut, was ich suche. Gibt es ein Ninject-Äquivalent?

Antwort

13

Ninject unterstützt vier eingebaute object scopes out of the box: Transient, Singleton, Thread, Request.

So gibt es keine PerResolveLifetimeManager wie Scope, aber Sie können es einfach mit der Registrierung eines benutzerdefinierten Bereichs mit der InScope Methode implementieren.

Wie sich herausstellte, gibt es eine existierende Ninject-Erweiterung: ninject.extensions.namedscope, die die InCallScope Methode bietet, wonach Sie suchen.

Allerdings, wenn Sie es selbst tun möchten, können Sie mit einem benutzerdefinierten InScope Delegat tun. Wo Sie das Haupt IRequest Objekt für den Typ verwenden können A sie als Bereichsobjekt zu verwenden:

var kernel = new StandardKernel(); 
kernel.Bind<A>().ToSelf().InTransientScope(); 
kernel.Bind<B>().ToSelf().InTransientScope(); 
kernel.Bind<C>().ToSelf().InTransientScope(); 
kernel.Bind<D>().ToSelf().InScope(
    c => 
     { 
      //use the Request for A as the scope object       
      var requestForA = c.Request; 
      while (requestForA != null && requestForA.Service != typeof (A)) 
      { 
       requestForA = requestForA.ParentRequest; 
      } 
      return requestForA; 
     }); 

var a1 = kernel.Get<A>();  
Assert.AreSame(a1.b.d, a1.c.d); 

var a2 = kernel.Get<A>();  
Assert.AreSame(a2.b.d, a2.c.d); 

Assert.AreNotSame(a1.c.d, a2.c.d); 

Wo die Probenklassen sind:

public class A 
{ 
    public readonly B b; 
    public readonly C c; 
    public A(B b, C c) { this.b = b; this.c = c; } 
} 

public class B 
{ 
    public readonly D d; 
    public B(D d) { this.d = d; } 
} 

public class C 
{ 
    public readonly D d; 
    public C(D d) { this.d = d; } 
} 

public class D { } 
+0

Wird das nicht brechen, wenn D auch Teil ist, wenn eine Abhängigkeitskette nicht A enthält? –

+0

@ KevinBabcock. Nein, es wird nicht brechen. Dann gibt der Delegat "null" zurück, was der folgenden Registrierung entspricht: 'kernel.Bind () .ToSelf(). InTransientScope();' – nemesv

+0

Danke für die Klarstellung. Gute Antwort! –

1

Eine Alternative, die Abhängigkeiten selbst zu konstruieren ist.

var kernel = new StandardKernel(); 
kernel.Bind<A>().ToMethod(ctx => 
{ 
    var d = new D(); 
    var c = new C(d); 
    var b = new B(d); 
    var a = new A(b, c); 
    return a; 
}); 

Dies kann nicht die bevorzugte Methode sein, aber es wird eine neue Instanz von A mit einer neuen Instanz von B, C und D (aber die Wiederverwendung der gleichen Instanz von D für B und C) immer konstruieren. Sie können einen Anruf zu InTransientScope() hinzufügen, aber das ist nicht erforderlich, da dies der Standardbereich ist.

+0

Das ist viel zu spezifisch für das, was ich brauche. Es gibt tatsächlich viele verschiedene Klassen auf den Ebenen A, B, C in meinem Code, die alle ein D für eine gegebene Auflösung teilen müssen. – RationalGeek

6

Ich fand die Lösung für mein spezifisches Problem, das InCallScope, das von der ninject.extensions.namedscope Erweiterung bereitgestellt wird. Dies verhält sich identisch mit dem Unity PerResolveLifetimeManager-Konzept.

Verwandte Themen