Dependency Injection und IoC Container

Klassische Auflösung von Abhängigkeiten

Abhängigkeitsgraph einer Klassenbibliothek
Klassisch wird der Abhängigkeitsgraph von den Klassen selber aufgelöst:
Die Implementierung einer Klasse A nutzt Dienste einer Klasse B. Dazu kann A eine Instanz von B selbst erzeugen: class A{ B b; A() { b = new B(); }}.
Nachteile:
  1. Ist B schwer testbar, da zur Laufzeit an einen bestimmten Kontext gebunden (z.B. Datenbank), wird auch A schwer testbar.
  2. Ersatz von B durch ein dekoriertes B nicht möglich

Auflösung mittels Dependency Injection

Dependency Injection (DI): Abhängigkeiten einer Klasse werden über die Parameterliste des Konstruktors aufgelöst:
Die Implementierung einer Klasse A nutzt Dienste einer Klasse B. Dazu kann A eine Instanz von B als Parameter vom Konstruktor erhalten, wobei gilt, dass B die Schnittstelle IB implementiert, und A letztendlich von dieser Schnittstelle abhängt: class A{ IB b; A(IB b) { this.b = b; }} var a = new A(new B())
Vorteile:

  1. Die originale Klasse B kann zu Testzecken durch die Klasse BMockUp ersetzt werden, die ebenfalls die Schnittstelle IB implementiert
  2. A funktioniert auch mit belibigen Dekoratoren von IB, solange das Liskovsche Substitutionsprinzip erfüllt ist.

Eine Klasse ist kann ihre Abhängigkeiten nicht nur über den Konstruktor injeziert bekommen. Alternativen sind:

Diese Differenzierungen der Dependency- Injection gehen auf eine Veröffentlichung von Martin Fowler zurück: Inversion Control of Containers and Dependency injection pattern

IoC: Inversion of Control

Prinzip der IoC

Poor man's Dependency injection

Hierbei handelt es sich um die "manuelle" Auflösung des Abhängigkeitsgraphen bei Anwendung von DI, dh. ohne weitere Hilfsmittel. Dabei werden

IoC- Bibliotheken: Unity vs. MEF

Unter .NET stehen zwei Implementierungen für IoC - Container bereit:

  1. Unity: leichtgewichtiger IoC, dadurch geeignet für Webanwendungen
  2. MEF = Microsoft Extensibillity Framework: ab. .NET 4.0 Bestandteil des Frameworks
Ein vergleich beider Technologieen findet man hier. Folgende Tablle vergleicht beide Bibliotheken:
Feature Unity MEF
POCOS yes (no) ab 4.5 Auswahl von Klassen möglich -> defakto POCOS
Interception yes no

Unity IoC: Register, Resolve, Release

Unity

Anforderungen an Klassen für Teilnahme an Unity IoC

Klassen, die zur Auflösung von Schnittstellen in Unity registriert werden, sollten folgende Anforderung erfüllen:

  1. Sie sollten nur einen Konstruktor besitzen ...
  2. ... oder alternativ den Konstruktor, der vom IoC- Container beim Auflösen aufgerufen werden soll, mit dem Attribut [InjectionConstructor] markieren.
public class User : IUser
{
        
    [InjectionConstructor]
    public User() { }
    public User(IUser user)
    {
        Name = user.Name;
        Scripts = user.Scripts;
    }
    public string Name
    {
        get;            
        set;
    }
    // ...
}

Auflösung mit Konstruktoren, die nichtleere Parameterlisten haben

Hat ein Konstruktor eines Typs T, der für die Auflösung einer Schnittstellt IT registriert wurde, eine nichtleere Parameterliste T(a, b, ...), dann können diese an Werte gebunden werden mittels der Klasse InjectionConstructor:

    // Achtung: Mock.User hat zwei Konstruktoren. Mittels Attribut InjectionController
    // wird einer für die DI- ausgewählt
    IoCContainer.RegisterType(new InjectionConstructor("unbekannt"));

Verwaltung der Lifetime

Details zur Verwaltung der Lebenszeit von Objekten, die durch Auflösung von Abhängigkeiten mittels Unity entstanden, finden sich hier.

Standardeinstellung ist das Anlegen neuer Instanzen mit jedem container.Resolve<IType>().
Indem als Parameter eine Instanz vom Typ ContainerControlledLifetimeManager beim Aufruf von container.Resolve<IType>() übergeben wird, werden pro aufgelösten Typ jeweils ein Singletons angelegt und bei weiteren Resolves ausgeliefert.

    // Achtung: die Repositorys sollen als Singletons ausgewählt werden. Dazu ist ein spezieller 
    // Lifetime- Manager zu setzen
    // https://msdn.microsoft.com/de-de/library/dn178463(v=pandp.30).aspx#_Lifetime_Management
    IoCContainer.RegisterType(new Microsoft.Practices.Unity.ContainerControlledLifetimeManager());
    IoCContainer.RegisterType(new Microsoft.Practices.Unity.ContainerControlledLifetimeManager());

Unity in ASP.NET MVC

Konfiguration hier

Unity in ASP.NET WebApi

Konfiguration der Dependency- Injection in der WebApi