2012-11-03 20 views
7

Próbuję wprowadzić właściwość za pomocą ninject. Biorąc pod uwagę dwa wiązania w poniższym module ninject, oczekiwałbym, że ConcreteDependency zostanie wstrzyknięty do B.
Wygląda jednak na to, że WhenInjectedInto nie uwzględnia typu, w który wtryskiwany jest tylko typ deklarowanego celu (właściwość w tym przypadku).Zastrzyk własności własności wtryskowej w klasie bazowej

Czy istnieje sposób na zachowanie, którego się spodziewałem?

static void Main(string[] args) 
{ 
    var kernel = new StandardKernel(new TestModule()); 
    var b = kernel.Get<B>(); 
    var c = kernel.Get<C>(); 
} 

class TestModule : NinjectModule 
{ 
    public override void Load() 
    { 
     Bind<IDependency>().To<EmptyDependency>(); 
     Bind<IDependency>().To<ConcreteDependency>().WhenInjectedInto<B>(); 
    } 
} 

abstract class A 
{ 
    [Inject] 
    public IDependency Dependency { get; set; } 
} 

class B : A {} 

class C : A {} 

interface IDependency {} 

class EmptyDependency : IDependency { } 

class ConcreteDependency : IDependency { } 
+0

Jak zarejestrować A, B i C w NInject? –

+0

Nie musisz w Ninject. Ponieważ 'B' i' C' są konkretnymi klasami, nie muszą być jawnie rejestrowane. – Gibsnag

Odpowiedz

4

Aby sprawdzić typ konkretnego betonu, można użyć interfejsu ParentContext.Plan.Type w interfejsie IRequest. To powinno dać ci zachowanie, którego oczekiwałeś od WhenInjectedInto. Na przykład:

When(req => req.ParentContext.Plan.Type == typeof(B)) 
8

Jeśli to możliwe, należy użyć zastrzyku konstruktora zamiast wstrzyknięcia własności. Jest to lepsza technika zalecana przez Marka Seemana, ponieważ sprawia, że ​​zależności wymagane do konstrukcji obiektu jawnego i podpisu obiektu przez konstruktora są bardziej wyraziste. Kod powinien wyglądać tak:

abstract class A 
    { 
     public IDependency Dependency { get; private set; } 

     public A (IDependency dependency) 
     { 
      Dependency = dependency; 
     } 

    } 

    class B : A 
    { 
     public B (IDependency dependency) 
      : base(dependency) 
     { 

     } 
    } 

    class C : A 
    { 
     public C (IDependency dependency) 
      : base(dependency) 
     { 

     } 
    } 

    interface IDependency { } 

    class EmptyDependency : IDependency { } 

    class ConcreteDependency : IDependency { } 

Konfiguracja będzie taka sama jak w przykładzie. Następujący test przechodzi

[Test] 
    public void TestSpecificBindingToObjectB() 
    { 
     var kernel = new StandardKernel(new TestModule()); 
     var b = kernel.Get<B>(); 
     var c = kernel.Get<C>(); 

     Assert.AreNotEqual(b.Dependency.GetType(), c.Dependency.GetType()); 
     Assert.AreEqual(typeof(ConcreteDependency), b.Dependency.GetType()); 
    } 

Jeśli masz opcjonalną zależność z domyślnej implementacji i jesteś ok z dekoracji klas z atrybutem Inject, można można wyciągnąć nadrzędnego informacji z wniosku, tak:

class TestModule : NinjectModule 
{ 
    public override void Load() 
    { 
     Bind<IDependency>().To<EmptyDependency>(); 
     Bind<IDependency>().To<ConcreteDependency>().When(req =>req.ParentContext.Request.Service == typeof(B)); 
    } 
} 

Następnie ten sam test podany powyżej przechodzi dla hierarchii klas z wtryskiem własności.

+1

Dziękuję, ale niestety chciałbym, żeby to działało na wtrysk nieruchomości. Zależność w tym przypadku jest czymś, co może mieć rozsądną wartość domyślną (pustą zależność), więc przenosiłem ją z wtrysku konstruktora na wtrysk właściwości, aby uniknąć zbyt wielu parametrów konstruktora. – Gibsnag

+1

Próbowałem użyć metody rozszerzającej 'When' z predykatem, który używałby typu, który jest wprowadzany, zamiast typu deklarującego właściwości, ale nie mógł znaleźć sposobu uzyskania niezbędnych informacji z' IRequest'. – Gibsnag

+0

Zaktualizowałem swoją odpowiedź w odniesieniu do przypadku zależności opcjonalnej z domyślną implementacją –