2009-08-18 8 views
17

Ich bin ein Neuling, wenn es um DI und Ninject geht und ich habe ein wenig zu kämpfen über, wann die eigentliche Injektion passieren sollte und wie die Bindung zu starten.Ninject - wie und wann zu injizieren

Ich benutze es bereits in meiner Webanwendung und es funktioniert gut dort, aber jetzt möchte ich Injektion in einer Klassenbibliothek verwenden.

sagen, dass ich eine Klasse wie dieses:

public class TestClass 
{ 
    [Inject] 
    public IRoleRepository RoleRepository { get; set; } 
    [Inject] 
    public ISiteRepository SiteRepository { get; set; } 
    [Inject] 
    public IUserRepository UserRepository { get; set; } 

    private readonly string _fileName; 

    public TestClass(string fileName) 
    { 
     _fileName = fileName; 
    } 

    public void ImportData() 
    { 
     var user = UserRepository.GetByUserName("myname"); 
     var role = RoleRepository.GetByRoleName("myname"); 
     var site = SiteRepository.GetByID(15); 
     // Use file etc 
    } 

} 

Ich möchte Injektionseigenschaft verwenden hier, weil ich in einem Dateinamen in meinem Konstruktor übergeben müssen. Bin ich richtig, wenn ich sage, dass, wenn ich in einem Konstruktorparameter übergeben muss, ich Konstruktorinjektion nicht verwenden kann? Wenn ich Injektion Konstruktor mit zusätzlichen Parametern verwenden kann, tun, wie passiere ich die Parameter in?

Ich habe eine Konsolenanwendung, die von Test-Klasse verbraucht, das aussieht wie folgt:

class Program 
{ 
    static void Main(string[] args) 
    { 
     // NinjectRepositoryModule Binds my IRoleRepository etc to concrete 
     // types and works fine as I'm using it in my web app without any 
     // problems 
     IKernel kernel = new StandardKernel(new NinjectRepositoryModule()); 

     var test = new TestClass("filename"); 

     test.ImportData(); 
    } 
} 

Mein Problem ist, dass, wenn ich rufe test.ImportData() meine Repositories sind null - nichts hat sich in sie injiziert. Ich habe versucht, ein weiteres Modul zu schaffen und

Bind<TestClass>().ToSelf(); 

Aufruf als ich dachte, das könnte alle Injektionseigenschaften in TestClass lösen, aber ich bin immer nirgendwo.

Ich bin mir sicher, dass dies ein triviales Problem ist, aber ich kann einfach nicht herausfinden, wie das geht.

Antwort

18

Sie sind direkt newing TestClass, die Ninject keine Möglichkeit hat, abzufangen - denken Sie daran gibt keine Magie ist wie Transformation Code abfangen Ihre new s usw.

Sie sollten kernel.Get<TestClass> stattdessen tun.

dass Failing, können Sie es nach injizieren Sienew es mit einem kernel.Inject(test);

Ich denke, es ist ein Artikel im Wiki, die über Inject vs Get usw.

Beachten Sie, dass im Allgemeinen spricht, Direkt Get oder Inject Anrufe sind ein tun es falsch Geruch von Service Lage, die ein Antipattern ist. Im Fall Ihres Web-App, die NinjectHttpModule und PageBase sind die Haken, die Objekterstellung ab - gibt es Ähnliche Abfangjäger/logische Orte in anderen Arten von App abzufangen.

Re Ihre Bind<TestClass>().ToSelf(), in der Regel ein StandardKernel hat ImplicitSelfBinding = true was das überflüssig machen würde (es sei denn, Sie in ihrem Umfang beeinflussen wollen etwas anderes als .InTransientScope() zu sein).

Ein letzter Stilpunkt: - Sie verwenden Eigenschaft Injektion. Es gibt selten gute Gründe dafür, weshalb Sie stattdessen die Konstruktorinjektion verwenden sollten.

Und gehen Sie kaufen Dependency Injection in .NET by @Mark Seemann, die Stapel von ausgezeichneten Beiträge hier hat, die viele wichtige, aber subtile Überlegungen in und um die Dependency Injection-Bereich abdecken.

+0

Verwendung von kernel.Inject (Test) scheint zu arbeiten. Vielen Dank. Aus Neugier eine andere Frage - Ich bin TestClass neu, weil ich eine Zeichenfolge in den Konstruktor übergeben muss. Gibt es eine Möglichkeit, einen Parameter mit Kernel.Get () zu übergeben? –

+0

Ja - es gibt Überladungen von Get und Sie können sie auch in den Bind setzen –

+0

Im Allgemeinen ein besseres Patttern anstelle von hardwiring es so haben Sie die Sache, die den Konstruktor arg stattdessen Verweis ein Confuigurator-Objekt, das Sie neu und dann Bind Beim Erstellen des Moduls –

7

OK,

Ich habe herausgefunden, wie was zu tun ich brauche, zum Teil dank Ihre Kommentare Ruben. Ich habe ein neues Modul erstellt, das im Grunde die Konfiguration enthält, die ich in der Klassenbibliothek verwende. Innerhalb dieses Moduls kann ich entweder mit einer Platzhalter-Schnittstelle binden oder ich kann einen Konstruktor-Parameter zum CustomerLoader hinzufügen. Im Folgenden finden Sie den Code von einer Dummy-Konsolenanwendung zum Demonstrieren beider Möglichkeiten.

Dies könnte anderen helfen, mit Ninject zu beginnen!

using System; 
using System.Collections.Generic; 
using System.Linq; 
using System.Text; 
using Ninject.Core; 
using Ninject.Core.Behavior; 

namespace NinjectTest 
{ 
    public class Program 
    { 
     public static void Main(string[] args) 
     { 
      var kernel = new StandardKernel(new RepositoryModule(), new ProgramModule());    
      var loader = kernel.Get<CustomerLoader>(); 
      loader.LoadCustomer(); 
      Console.ReadKey(); 
     } 
    } 

    public class ProgramModule : StandardModule 
    { 
     public override void Load() 
     { 
      // To get ninject to add the constructor parameter uncomment the line below 
      //Bind<CustomerLoader>().ToSelf().WithArgument("fileName", "string argument file name"); 
      Bind<LiveFileName>().To<LiveFileName>(); 
     } 
    } 

    public class RepositoryModule : StandardModule 
    { 
     public override void Load() 
     { 
      Bind<ICustomerRepository>().To<CustomerRepository>().Using<SingletonBehavior>(); 
     } 
    } 

    public interface IFileNameContainer 
    { 
     string FileName { get; } 
    } 
    public class LiveFileName : IFileNameContainer 
    { 
     public string FileName 
     { 
      get { return "live file name"; } 
     } 
    } 


    public class CustomerLoader 
    { 
     [Inject] 
     public ICustomerRepository CustomerRepository { get; set; } 
     private string _fileName; 

     // To get ninject to add the constructor parameter uncomment the line below 
     //public CustomerLoader(string fileName) 
     //{ 
     // _fileName = fileName; 
     //} 
     public CustomerLoader(IFileNameContainer fileNameContainer) 
     { 
      _fileName = fileNameContainer.FileName; 
     } 

     public void LoadCustomer() 
     { 
      Customer c = CustomerRepository.GetCustomer(); 
      Console.WriteLine(string.Format("Name:{0}\nAge:{1}\nFile name is:{2}", c.Name, c.Age, _fileName)); 
     } 
    } 

    public interface ICustomerRepository 
    { 
     Customer GetCustomer(); 
    } 
    public class CustomerRepository : ICustomerRepository 
    { 
     public Customer GetCustomer() 
     { 
      return new Customer() { Name = "Ciaran", Age = 29 }; 
     } 
    } 
    public class Customer 
    { 
     public string Name { get; set; } 
     public int Age { get; set; } 
    } 
} 
+0

Sieht gut aus - das ist ziemlich genau das, was ich meinte. Im Zusammenhang mit einer realen App würde ich vermuten, dass der IFilenameContainer -Stuff stattdessen durch etwas allgemeines wie DocumentLocation und auf der obersten Ebene von Datei | Öffnen oder der Befehlszeile usw. aufgefüllt wird. Im Allgemeinen das Konzept, die Informationen, die übergeben werden, zu umhüllen als rohe Daten in Params in etwas, von dem man die Information von einer höheren Ebene bekommen kann, macht das Verwenden von Parametern weniger üblich (ich habe es nur einmal bei meinem letzten Projekt verwendet) –

Verwandte Themen