2012-05-18 5 views
14

Ich habe ein Objekt IConfig, das Einstellungen enthält, die in meiner gesamten Anwendung verwendet werden. Im Moment spritze ich das gesamte Objekt in den Konstruktor jedes Objekts, die es braucht, wie folgt:Ninject: Konstruktor-Argument an Eigenschaft eines anderen Objekts binden

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; 
    } 
} 

Der Nachteil ist, dass IConfig eine große Anzahl von Einstellungen enthält, weil es von einer Gesamtkonfigurationsdatei deserialised ist, so es ist unnötig, das gesamte Objekt einzuspeisen. Was ich tun möchte, ist, den Konstruktor in Foo(string username, string password) zu ändern, damit es nur die Einstellungen erhält, die es benötigt. Dies macht auch das Erstellen von Foo Objekten zum Testen viel einfacher (ohne IConfig einzurichten, nur um Foo zu erstellen). Ich möchte die Konstruktorargumente direkt in meinem NinjectModule, etwa wie folgt binden:

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); 
    } 
} 

Offensichtlich dieser Code nicht funktioniert, aber wie würde ich mich über das tun, was ich wollte?

Meine ursprüngliche Idee war, die NinjectModule.Kernel verwenden die IKernel dann bekommen Sie eine Instanz meines IConfig Objekt erhalten und die Eigenschaften injizieren, wie erforderlich, aber das Objekt zurück von NinjectModule.Kernel hat keine Get<T>() Methode.

Antwort

14

Sie sind auf dem richtigen Weg:

Die Kernel.Get<T>() Verfahren eine Erweiterungsmethode auf den ResolutionExtensions im Ninject namepsace definiert sind so mit den using Ninject; Zugabe es auch in Ihrem Modul zur Verfügung steht.

Aber statt der Module.Kernel Sie die IContext in der zweiten Überlastung von WithConstructorArgument vorgesehen verwenden sollte die Kernel zu bekommen:

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

Ich kann nicht scheinen, dass dies funktioniert. Das im Lambda erstellte IConfig-Objekt enthält alle Standardwerte (leere Strings für diese Einstellungen). Wenn ich nur einen normalen Kernel.Get jetzt, dass ich 'mit Ninject' hinzugefügt habe funktioniert das richtig. Sollte ich den Aufwand investieren, um die "Kontext" -Version zum Laufen zu bringen, gibt es einen Vorteil? –

+0

Es sollte mit 'context.Kernel' funktionieren, ich werde es mir anschauen. Wie werden die Werte in 'IConfig' verpuppt?Verwenden Sie mehrere Kernel? – nemesv

+0

Habe gerade ein einfaches Testprojekt erstellt und das funktioniert. Muss etwas sein, das für meine spezielle Lösung spezifisch ist. Vielen Dank! –

1

Dies könnte eine gute candiate für die Interface segregation principle.

In diesem Fall definieren eine weitere Schnittstelle wie etwa ein ICredentialConfig enthält nur die Username und Password Eigenschaften, dann IConfig diese Schnittstelle machen implementieren.

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

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

Jetzt Foo auf ICredentialConfig abhängig machen, anstatt IConfig. Sie können dann:

  1. spritzen Sie Ihre JsonConfig Ninject verwenden, statt fest codierten Parameternamen zu haben.
  2. Implementieren/Mock ein ICredentialConfig zum Instanziieren Foo in Tests, anstatt die vollständige IConfig Schnittstelle implementieren zu müssen.