2013-06-28 9 views
50

Ich habe ein paar Freunden bei einem Projekt geholfen und es gibt eine Klasse, die Ninject benutzt. Ich bin ziemlich neu in C# und ich habe keine Ahnung, was diese Klasse macht, weshalb ich Ninject verstehen muss. Kann jemand erklären was Ninject ist und wann benutzt man es (mit Beispiel wenn möglich)? Oder wenn Sie auf einige Links verweisen können, die auch toll wären.Was ist Ninject und wann benutzt du es?

Ich habe diese Frage versucht: Ninject tutorials/documentations?, aber es hat einem Anfänger wie mir nicht wirklich geholfen.

+5

ninject ist ein Abhängigkeits-Injection-Framework. fange hier an: https: // github.com/ninject/ninject/wiki/Dependency-Injection-by-Hand für eine gute Einführung in das Konzept und wie man ninject verwendet – drch

+0

Die Dokumentation im Wiki erklärt alles aus den Grundlagen. http://www.ninject.org/wiki.html – spender

Antwort

35

Ninject ist eine Inversion des Control-Containers.

Was macht es?

Angenommen, Sie haben eine Car Klasse, die von einer Driver Klasse abhängt.

public class Car 
{ 
    public Car(IDriver driver) 
    { 
     /// 
    } 
} 

Um die Car Klasse verwenden Sie es wie so bauen:

IDriver driver = new Driver(); 
var car = new Car(driver); 

A IoC containter zentralisiert das Wissen darüber, wie Klassen zu bauen. Es ist ein zentrales Repository, das ein paar Dinge kennt. Zum Beispiel weiß es, dass die konkrete Klasse, die Sie verwenden müssen, um ein Auto zu bauen, ein Driver und kein anderes IDriver ist.

Wenn Sie beispielsweise eine MVC-Anwendung entwickeln, können Sie Ninject sagen, wie Sie Ihre Controller erstellen. Dazu registrieren Sie, welche konkreten Klassen bestimmte Schnittstellen erfüllen. Zur Laufzeit wird Ninject herausfinden, welche Klassen benötigt werden, um den benötigten Controller zu bauen und alles hinter den Kulissen.

Dies ist vorteilhaft, da Sie damit Systeme erstellen können, die einfacher testbar sind. Angenommen, Driver kapselt den gesamten Datenbankzugriff für Car ein. In einer Unit-Test für Auto können Sie dies tun:

IDriver driver = new TestDriver(); // a fake driver that does not go to the db 
var car = new Car(driver); 

Es gibt ganze Frameworks, die darauf achten, Testklassen für Sie automatisch erstellen und sie verspotten Frameworks genannt.

Für weitere Informationen:

+0

Warum möchten Sie eine Testklasse? Wenn Sie eine Entwicklungsumgebung haben, warum möchten Sie nicht zur Datenbank gehen? Ich habe an Projekten mit Ninject gearbeitet und kann den Vorteil nicht sehen. Wir ändern niemals die konkrete Klasse, es ist immer dieselbe. – Kelly

45

Ninject ist Abhängigkeits Injektors für NET, praktische Realisierung Muster-Abhängigkeit Injection (Form Inversion des Kontrollmusters n).

Angenommen, Sie haben zwei Klassen DbRepository und Controller:

class Controller { 
    private DbRepository _repository; 

    // ... some methods that uses _repository 
} 

class DbRepository { 
    // ... some bussiness logic here ... 
} 

So, jetzt haben Sie zwei Probleme:

  1. Sie _repository es zu benutzen inialize müssen.Sie haben solche Möglichkeiten:

    1. Im Konstruktor manuell. Was aber, wenn sich der Konstruktor des DbRepository ändert? Sie müssen Ihre Controller Klasse neu schreiben, da der andere Teil des Codes geändert wurde. Es ist nicht schwer, wenn Sie nur eine haben Controller, aber wenn Sie ein paar Klassen haben, die Abhängigkeit von Ihrem Repository haben Sie ein echtes Problem.
    2. Sie können Service Locator oder Fabrik oder SMS verwenden. Jetzt haben Sie eine Abhängigkeit von Ihrem Service Locator. Sie haben einen globalen Service-Locator und der gesamte Code muss ihn verwenden. Wie ändern Sie das Verhalten des Service Locators, wenn Sie in einem Teil des Codes eine Aktivierungslogik und etwas anderes in einem anderen Teil des Codes verwenden müssen? Es gibt nur einen Weg - Service Locator durch die Konstruktoren zu übergeben. Aber mit mehr und mehr Klassen müssen Sie es mehr und mehr weitergeben. Wie auch immer, das ist eine nette Idee, aber es ist eine schlechte Idee.

      class Controller { 
          private DbRepository _repository; 
      
          public Controller() { 
          _repository = GlobalServiceLocator.Get<DbRepository>() 
          } 
      
          // ... some methods that uses _repository 
      } 
      
    3. Sie können die Abhängigkeitsinjektion verwenden. Schauen Sie sich den Code:

      class Controller { 
          private IRepository _repository; 
      
          public Controller(IRepository repository) { 
           _repository = repository; 
          } 
      } 
      

    Nun, wenn Sie Ihren Controller müssen Sie schreiben: ninjectDevKernel.Get<Controller>(); oder ninjectTestKernel.Get<Controller>();. Sie können so schnell wie möglich zwischen Abhängigkeits-Resolvern wechseln. Sehen? Es ist einfach, Sie müssen nicht viel schreiben.

  2. Sie können keine Unit-Tests durchführen. Ihre Controller haben Abhängigkeit von DbRepository und wenn Sie eine Methode testen möchten, die Repository verwendet, wird Ihr Code an die Datenbank gehen und sie nach Daten fragen. Das ist langsam, sehr langsam. Wenn sich Ihr Code in DbRepository ändert, wird Ihr Gerätetest unter Controller fallen. Nur Integrationstest muss in diesem Fall "Probleme" sagen. Was Sie in Unit Tests benötigen - um Ihre Klassen zu isolieren und nur eine Klasse in einem Test zu testen (im Idealfall - nur eine Methode). Wenn Ihr DbRepository Code fehlschlägt, werden Sie denken, dass Controller Code fehlgeschlagen ist - und das ist schlecht (auch wenn Sie Tests für DbRepository und Controller haben - beide werden fehlschlagen und Sie können von falschem Ort starten). Es braucht viel Zeit, um festzustellen, wo der Fehler wirklich liegt. Sie müssen wissen, dass eine Klasse in Ordnung ist und eine andere Klasse fehlgeschlagen ist.

  3. Wenn Sie DbRepository durch etwas anderes in all Ihren Klassen ersetzen möchten, müssen Sie eine Menge Arbeit erledigen.

  4. Sie können nicht einfach Lebensdauer von DbRepository steuern. Objekt dieser Klasse erzeugt beim Initialisieren Controller und löscht, wenn Controller löscht. Es gibt keine Freigabe zwischen verschiedenen Instanzen der Klasse Controller und es gibt keine Freigabe zwischen anderen Klassen. Mit Ninject können Sie einfach schreiben:

    kernel.Bind<IRepository>().To<DbRepository>().InSingletonScope(); 
    

Und Besonderheit Dependency Injection - agile Entwicklung! Sie beschreiben, dass Ihr Controller das Repository mit der Schnittstelle IRepository verwendet. Sie müssen nicht DbRepository schreiben, können Sie einfach schreiben MemoryRepository Klasse und entwickeln Controller während andere Jungs DbRepository entwickelt. Wenn DbRepository beendet wird, bindest du gerade in deinem Abhängigkeitsresolver, IRepository ist jetzt DbRepository. Sie haben viele Controller geschrieben? Alle von ihnen werden jetzt DbRepository verwenden. Das ist cool.

Lesen Sie mehr:

  1. Inversion of control (wiki)
  2. Dependency injection (wiki)
  3. Inversion of Control Containers and the Dependency Injection pattern (Martin Fowler)
+2

Ich verstehe immer noch nicht, wie Ninject es einfacher machen würde, als nur manuell zu injizieren. Wenn ich eine Upload-Dateistrategie für jedes Unternehmen habe und die Strategie zur Laufzeit auf der Grundlage des ausgewählten Unternehmens geladen werden muss. Es scheint, als würde ninject nicht viel vom Code verändern, egal ob ich es manuell mache oder Ninject benutze. Die einzigen Vorteile, die ich von Ninject sehe, ist, wenn ich während des Testens in eine Datenbank schreiben oder mich in eine Textdatei einloggen möchte, dann kann ich die Strategie beim Start leicht ändern. – MIKE

+1

Sogar dafür können Sie eine abstrakte Fabrik verwenden, um dasselbe zu erreichen. Die facory kann eine Methode wie LoadAssemblyAndUnwrap oder GetType aufrufen, um eine Instanz des richtigen Typs zu erstellen. Die zu verwendende Assembly oder der Typ kann in einer Konfigurationsdatei gespeichert werden. Also keine Notwendigkeit, sich auf eine andere Bibliothek, Ninject, Unity oder solche Dinge zu verlassen. :) – gWay

+0

Ninject und andere DI-Container sind in zwei Fällen nützlich: 1) Zu viele Abhängigkeiten, um alle zusammen zu binden. Declarative Config ist besser lesbar und schreibbar 2) Wenn Sie nicht wissen, Typ in Kompilierzeit aufzulösen (z. B. Asp-Controller zur Laufzeit instanziiert sind und Sie wirklich viel haben können dann –

2

Andere Antworten sind toll, aber ich würde auch diesen Implementing Dependency Injection using Ninject Artikel weisen darauf hin,.
Dies ist einer der besten Artikel, die ich je gelesen habe, die Dependency Injection und Ninject mit einem sehr eleganten Beispiel erklärt.

Hier ist der Ausschnitt aus dem Artikel:

Below-Schnittstelle wird von unserer (SMSService) umgesetzt werden und (MockSMSService), im Grunde das neue Interface (ISMSService) die gleichen Verhaltensweisen beiden Dienste wie der Code aussetzen unten:

public interface ISMSService 
{ 
void SendSMS(string phoneNumber, string body); 
} 

(SMSService) Umsetzung der (ISMSService) Schnittstelle zu implementieren:

public class SMSService : ISMSService 
{ 
public void SendSMS(string mobileNumber, string body) 
{ 
SendSMSUsingGateway(mobileNumber, body); 
} 




private void SendSMSUsingGateway(string mobileNumber, string body) 
{ 
/*implementation for sending SMS using gateway*/ 
Console.WriteLine("Sending SMS using gateway to mobile: 
    {0}. SMS body: {1}", mobileNumber, body); 
} 
} 

(MockSMSService) mit völlig unterschiedlichen Umsetzung die gleiche Schnittstelle:

public class MockSMSService :ISMSService 
{ 
public void SendSMS(string phoneNumber, string body) 
{ 
SaveSMSToFile(phoneNumber,body); 
} 

private void SaveSMSToFile(string mobileNumber, string body) 
{ 
/*implementation for saving SMS to a file*/ 
Console.WriteLine("Mocking SMS using file to mobile: 
    {0}. SMS body: {1}", mobileNumber, body); 
} 
} 

brauchen wir eine Änderung unserer (UIHandler) Klassenkonstruktors zu implementieren, um die Abhängigkeit durch sie passieren, indem Sie dies tun, der Code, der verwendet die (UIHandler) kann die konkrete Umsetzung von (ISMSService) bestimmen zu verwenden:

public class UIHandler 
{ 
private readonly ISMSService _SMSService; 

public UIHandler(ISMSService SMSService) 
{ 
_SMSService = SMSService; 
} 
public void SendConfirmationMsg(string mobileNumber) { 

_SMSService.SendSMS(mobileNumber, "Your order has been shipped successfully!"); 
} 
} 

Jetzt haben wir eine eigene Klasse erstellen müssen (NinjectBindings), die von (NinjectModule erbt). Diese Klasse ist dafür zuständig, Abhängigkeiten zur Laufzeit aufzulösen. Anschließend überschreiben wir das load-Ereignis, das zur Konfiguration der darin enthaltenen Bindung verwendet wird. Das Schöne an Ninject ist, dass wir unseren Code in (ISMSService), (SMSService) und (MockSMSService) nicht ändern müssen.

public class NinjectBindings : Ninject.Modules.NinjectModule 
{ 
public override void Load() 
{ 
Bind<ISMSService>().To<MockSMSService>(); 
} 
} 

nun in UI Form Code, werden wir die Bindung für Ninject verwenden, die verwendet werden, die Implementierung bestimmen,:

class Program 
{ 
static void Main(string[] args) 
{ 
IKernel _Kernal = new StandardKernel(); 
_Kernal.Load(Assembly.GetExecutingAssembly()); 
ISMSService _SMSService = _Kernal.Get<ISMSService>(); 

UIHandler _UIHandler = new UIHandler(_SMSService); 
_UIHandler.SendConfirmationMsg("96279544480"); 

Console.ReadLine(); 
} 
} 

der Code der Ninject Kernal wird nun mit aller Kette von Abhängigkeiten zu lösen Wenn wir den echten Dienst (SMSService) im Freigabemodus (in der Produktionsumgebung) anstelle des Scheindienstes verwenden möchten, müssen wir die Ninject-Bindungsklasse (NinjectBindings) nur ändern, um die richtige Implementierung zu verwenden, oder indem wir #if verwenden DEBUG-Richtlinie wie folgt:

public class NinjectBindings : Ninject.Modules.NinjectModule 
{ 
public override void Load() 
{ 
#if DEBUG 
Bind<ISMSService>().To<MockSMSService>(); 
#else 
Bind<ISMSService>().To<SMSService>(); 
#endif 

} 
} 

Jetzt lebt unsere Bindungsklasse (NinjectBindings) ganz oben auf unserem Ausführungscode und wir können die Konfiguration auf einfache Weise an einem einzigen Ort steuern.


Auch finden What is Inversion of Control? einige sehr einfache Beispiele genannt IoC zu verstehen.

Verwandte Themen