2012-05-18 11 views
14

Mam obiekt IConfig zawierający ustawienia używane w całej mojej aplikacji. W tej chwili mogę wstrzyknąć cały obiekt do konstruktora każdego obiektu, który potrzebuje go w następujący sposób:Rzek: powiąż argument konstruktora z właściwością innego obiektu

public interface IConfig 
{ 
    string Username { get; } 
    string Password { get; } 
    //... other settings 
} 

public class Foo : IFoo 
{ 
    private readonly string username; 
    private readonly string password; 

    public Foo(IConfig config) 
    { 
     this.username = config.Username; 
     this.password = config.Password; 
    } 
} 

Minusem jest to, że IConfig zawiera dużą liczbę ustawień, ponieważ jest deserialised od ogólnego pliku konfiguracyjnego, więc wstrzyknięcie całego obiektu jest niepotrzebne. Chciałbym zmienić konstruktor na Foo(string username, string password), aby otrzymał on tylko potrzebne ustawienia. Powoduje to również, że tworzenie obiektów do testowania jest o wiele łatwiejsze (nie trzeba konfigurować IConfig tylko po to, aby utworzyć). Chciałabym związać argumenty konstruktora bezpośrednio w moim NinjectModule, coś jak następuje:

public class MyModule : NinjectModule 
{ 
    public override void Load() 
    { 
     Bind<IConfig>().To<JsonConfig>() 
      .InSingletonScope(); 

     Bind<IFoo>().To<Foo>() 
      .WithConstructorArgument("username", IConfig.Username) 
      .WithConstructorArgument("password", IConfig.Password); 
    } 
} 

Oczywiście ten kod nie działa, ale jak bym go o to, co chciałem robić?

Mój oryginalny pomysł był użyć NinjectModule.Kernel uzyskać IKernel następnie uzyskać instancję moim IConfig obiektu i wstrzyknąć właściwości wymagane, ale obiekt zwrócony przez NinjectModule.Kernel ma Get<T>() metody.

Odpowiedz

14

Jesteś na właściwej drodze:

Sposób Kernel.Get<T>() jest metoda rozszerzenie określone na ResolutionExtensions w Ninject namepsace więc z dodaniem using Ninject; jest ona dostępna w module, jak również.

Ale zamiast Module.Kernel należy użyć IContext przewidzianego w drugim przeciążenia WithConstructorArgument uzyskać Kernel:

Bind<IFoo>().To<Foo>() 
    .WithConstructorArgument("username", 
          context => context.Kernel.Get<IConfig>().Username) 
    .WithConstructorArgument("password", 
          context => context.Kernel.Get<IConfig>().Password); 
+1

Nie mogę sprawić, żeby to zadziałało. Obiekt 'IConfig' utworzony w lambda zawiera wszystkie domyślne wartości (puste ciągi dla tych ustawień). Jeśli po prostu zrobię normalny Kernel.Get teraz, gdy dodałem 'using Ninject', to działa poprawnie. Czy powinienem zainwestować wysiłek, aby uzyskać działanie wersji 'context', czy jest jakaś zaleta? –

+0

Powinno to działać z 'context.Kernel', będę musiał rzucić okiem na to. Jak postrzegane są wartości w "IConfig"?Używasz wielu jąder? – nemesv

+0

Właśnie stworzyłem prosty testowy projekt i to działa. To musi być coś konkretnego dla mojego konkretnego rozwiązania. Dzięki! –

1

To może być dobry candiate dla Interface segregation principle.

W tym przypadku określenie innego interfejsu, takiego jak ICredentialConfig zawierającym jedynie Username i Password właściwości, a następnie dokonać IConfig realizacji tego interfejsu.

public Interface ICredentialConfig 
{ 
    string Username { get; } 
    string Password { get; } 
} 

public Interface IConfig : ICredentialConfig 
{ 
    //... other settings 
} 

teraz zrobić Foo zależne ICredentialConfig zamiast IConfig. Następnie można:

  1. wstrzykiwać JsonConfig użyciu Ninject, zamiast jawnie wpisanej nazwy parametrów.
  2. Zaimplementuj/wyśmiewanie ICredentialConfig do tworzenia w testach Foo zamiast konieczności implementowania pełnego interfejsu IConfig.
Powiązane problemy