2017-01-11 5 views
1

Beim Erstellen einer Anwendung mit Dependency Injection und es verwendet ein Framework für Dependency Injection wie Unity (oder Ninject).
Wie initialisierst du die Registrierung der Schnittstellen zu dem Container am Anfang alle zusammen und hält sie für die Anwendung während des gesamten Lebenszyklus der Anwendung verfügbar?
Müssen Sie den DI-Container an jede Methode übergeben, die die Abhängigkeitsinjektion verwenden kann, oder gibt es eine Möglichkeit, den Container global zugänglich zu machen, sodass Sie sie alle zusammen am Anfang registrieren und während des gesamten Prozesses darauf zugreifen können um sie kontinuierlich zu passieren und sie zu nutzen, wann immer sie gebraucht werden?Abhängigkeitsinjektionscontainer - Wie verfügbar bleiben

Umwelt: Visual Studio 2015, C#, Microsoft Unity (für DI Container)

Beispielcode

static void Main(string[] args) 
    { 

     // Make Unity resolve the interface, providing an instance 
     // of TrivialPursuit class 
     var diContainer = new UnityContainer(); 
     diContainer.RegisterType<IGame, TrivialPursuit>(); 

     var gameInstance = diContainer.Resolve<IGame>(); 


     var xotherClass = new AnotherClass(); 
     xotherClass.TestOtherClassOtherMethod(); 

    } 

------ Eine andere Klasse ohne Zusammenhang mit der Injektion Klasse Abhängigkeit --- ---

public void TestOtherClassOtherMethod() 
    { 
     IGame gameInstance = -- -Container is Not available to resolve from in this class --- 
    } 

Grund: ich möchte nicht jede mögliche Art passieren müssen, dass ich zu jeder Klasse später brauchen kann ich laden, ich will nur die Instanzen verwenden, wenn ich sie brauche. Je tiefer ich in Klassen eindringe, desto später, wenn die Anwendung komplexer wird, will ich keine Instanzen für jeden Typ von der Main() -Methode an jede Klasse weiterleiten.

+2

Welche Art von Anwendung entwickeln Sie? Netz? Desktop? –

+0

Desktop (Konsolen-App), um es zu testen, und dann Web (MVC), sobald ich die Konzepte runter. – CodingRiot

Antwort

2

Ein Dependency Injection (DI) Container ist genau das. Ein Framework zur Erleichterung von DI. Sie übergeben den Container nicht, um Instanzen von Objekten aufzulösen. Sie fordern nur den Typ an, den Sie in Ihrem Klassenkonstruktor benötigen, und das DI-Framework injiziert die entsprechende Abhängigkeit.

Mark Seemann hat eine gute book on dependency injection geschrieben, die ich empfehlen würde.

Sie registrieren alles, was mit dem Container im Kompositionsstamm aufgelöst werden muss. Das heißt, wenn Ihr Programm startet, sollte alles registriert werden.

Lassen Sie uns sagen, dass wir den folgenden Code haben:

public class MyClass 
{ 
    public Run() 
    { 
     var dependency = new Dependency1(); 
     dependency.DoSomething(); 
    } 
} 

public class Dependency1 
{ 
    public void DoSomething() 
    { 
     var dependency = new Dependency2(); 
     dependeny.DoSomethingElse(); 
    } 
} 

public class Dependency2 
{ 
    public void DoSomethingElse() 
    { 
    } 
} 

Dies gibt uns die oben Abhängigkeitskette: MyClass -> abhaengigkeit1 -> Dependency2.

Das erste, was wir tun sollten, ist Refactoring der Klassen ihre Abhängigkeiten durch ihren Konstruktor zu nehmen und auf Schnittstellen statt Konkretionen beruhen. Wir können keine Abhängigkeiten einfügen, es sei denn, es gibt einen Platz, um sie zu injizieren (Konstruktor, Eigenschaft usw.). Hier

ist die Überarbeitete Code:

public interface IMyClass 
{ 
    void Run(); 
} 

public interface IDependency1 
{ 
    void DoSomething(); 
} 

public interface IDependency2 
{ 
    void DoSomethingElse(); 
} 

public class MyClass : IMyClass 
{ 
    public readonly IDependency1 dep; 

    public MyClass(IDependency1 dep) 
    { 
     this.dep = dep; 
    } 

    public void Run() 
    { 
     this.dep.DoSomething(); 
    } 
} 

public class Dependency1 : IDependency1 
{ 
    public readonly IDependency2 dep; 

    public MyClass(IDependency2 dep) 
    { 
     this.dep = dep; 
    } 

    public void DoSomething() 
    { 
     this.dep.DoSomethingElse(); 
    } 
} 

public class Dependency2 : IDependency2 
{ 
    public void DoSomethingElse() 
    { 
    } 
} 

Hier finden Sie die Klassen bemerken jetzt alle nehmen ihre Abhängigkeiten durch ihre Konstrukteure und nicht neu bis nichts. Klassen sollten nur Abhängigkeiten aufnehmen, die sie tatsächlich benötigen. Zum Beispiel benötigt MyClass keine Dependency2, so dass sie nicht nach einem fragt. Es fragt nur nach einer Abhängigkeit1, weil das alles ist, was es braucht. Abhängigkeit1 NEEDS Dependency2, nicht MyClass.

Jetzt alles auf, ohne einen Behälter verdrahten, wir würden in der Zusammensetzung Wurzel gerade neu es auf dem Punkt:

void Main() 
{ 
    var myClass = new MyClass(new Dependency1(new Dependency2())); 
} 

Sie sehen können, wie das cumbersom bekommen könnte, wenn wir Tonnen von Klassen und depdencies haben. Deshalb benutzen wir einen Container. Es behandelt alle Dependenz-Graphen für uns. Mit einem Behälter würden wir es wie folgt umschreiben:

void Main() 
{ 
    // the order of our registration does not matter. 
    var container = new Container(); 
    container.Register<IDependency1>.For<Dependency1>(); 
    container.Register<IDependency2>.For<Dependency2>(); 
    container.Register<IMyClass>.For<MyClass>(); 

    // then we request our first object like in the first example (MyClass); 
    var myClass = container.Resolve<IMyClass>(); 

    myClass.Run(); 
} 

Im zweiten Beispiel wird der Behälter entsprechend verdrahteten alle Abhängigkeiten behandeln. Daher müssen wir Depetency2 niemals zu MyClass und Depeption1 weiterleiten. Wir müssen es nur in Dependency1 anfordern und der Container wird es für uns wie im ersten Beispiel verdrahten.

So in Ihrem Beispiel würden wir es wie so umschreiben:

static void Main(string[] args) 
{ 
    var game = new UnityContainer(); 
    game.RegisterType<IGame, TrivialPursuit>(); 
    game.RegisterType<IAnotherClass, AnotherClass>(); 
    game.RegisterType<IYetAnotherClass, YetAnotherClass>(); 

    var gameInstance = game.Resolve<IGame>(); 
    // you'll need to perform some action on gameInstance now, like gameInstance.RunGame() or whatever. 
} 

public class Game : IGame 
{ 
    public Game(IAnotherClass anotherClass) 
    { 
    } 
}  

public class AnotherClass : IAnotherClass 
{ 
    public AnotherClass(IYetAnotherClass yetAnotherClass) 
    { 
    } 
} 

public class YetAnotherClass : IYetAnotherClass {} 

In diesen Fällen gibt es keine Notwendigkeit, den Behälter herum passieren. Sie registrieren Ihre Abhängigkeiten mit dem Container und fordern sie dann in Ihren Klassenkonstruktoren an. Wenn Sie den Container in der Klasse verwenden möchten, ohne ihn über den Konstruktor anzufordern, dann tun Sie nicht DI, sondern verwenden den Container nur als Singleton-Service-Locator. Etwas, das generell vermieden werden sollte.

Container als Service Locator Diese sollten generell vermieden werden, aber wenn Sie den Behälter als Service-Locator verwenden möchten, haben Sie zwei Möglichkeiten:

1) Führen Sie den Behälter in die Klassen, die sie benötigen, um durch der Konstruktor. Sie können die obigen Beispiele verwenden, um Ihre Klassen für DI zu verkabeln. Aber anstatt eine Abhängigkeit wie IDependency im Konstruktor anzufordern, übergeben Sie einfach den Container.

public class Game : IGame 
{ 
    public Game(IContainer container) 
    { 
     var blah = container.Resolve<IBlah>(); 
    } 
} 

2) Fordern Sie Ihren Behälter durch eine statische Klasse:

public static class ServiceLocator 
{ 
    private static IContainer container; 
    public static IContainer Container 
    { 
     get 
     { 
      if (container == null) 
      { 
       container = new Container(); 
      } 

      return container; 
     } 
    } 
} 

alles normal Registrieren Sie sich in Ihrer Komposition Wurzel der Servicelocator-Klasse. Dann zu verwenden:

public class MyClass 
{ 
    public void DoSomething() 
    { 
     var blah = ServiceLocator.Container.Resolve<IBlah>(); 
    } 
} 
+1

Ich denke, Sie haben den Punkt der Frage verpasst. Der Punkt war nicht "Was ist DI?" es war "Wie behalte ich den Container für zukünftige Operationen?". Nehmen wir zum Beispiel den Kontext einer MVC-App, der wahrscheinlich über den MVC-Abhängigkeits-Resolver bekannt sein muss. Deshalb ist Darin's Kommentar wichtig. – DavidG

+0

Außerhalb der Main() -Methode, wie würden Sie diese Instanz verwenden, da ihr Gültigkeitsbereich darauf beschränkt ist, nur innerhalb der Main() -Methode verwendet zu werden, müssen Sie diesen Container oder einen Verweis nicht weitergeben, um ihn verwenden zu können es anderswo? (Tut mir leid, ich lerne immer noch nur die Abhängigkeitsinjektion und möchte sicherstellen, dass ich es verstehe, bevor ich versuche, es vollständig zu nutzen). Vielen Dank für Ihre Hilfe. – CodingRiot

+1

Mein Punkt ist, dass Sie im Rest Ihrer App nichts über den Container wissen müssen. Nur der Kompositionswurzel, zu dem der Abhängigkeits-Resolver gehört. Sie fragen nicht nach Referenzen auf den Container woanders, fragen Sie einfach nach den tatsächlichen Abhängigkeiten, die Sie benötigen. Der Container wird sie dir geben, wenn sie registriert sind –