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:
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:
Eine Klasse ist kann ihre Abhängigkeiten nicht nur über den Konstruktor injeziert bekommen. Alternativen sind:
class A{ MethodX(IB b, ...) { b.op(...); ...}} var a = new A(); var b = new B(); a.MethodX(b, ...);
class A{ IB b; PropX{ set{ b = value}} var a = new A(); var b = new B(); a.PropX = b;
Hierbei handelt es sich um die "manuelle" Auflösung des Abhängigkeitsgraphen bei Anwendung von DI, dh. ohne weitere Hilfsmittel. Dabei werden
Unter .NET stehen zwei Implementierungen für IoC - Container bereit:
Feature | Unity | MEF |
---|---|---|
POCOS | yes | (no) ab 4.5 Auswahl von Klassen möglich -> defakto POCOS |
Interception | yes | no |
Klassen, die zur Auflösung von Schnittstellen in Unity registriert werden, sollten folgende Anforderung erfüllen:
[InjectionConstructor]
markieren.
public class User : IUser { [InjectionConstructor] public User() { } public User(IUser user) { Name = user.Name; Scripts = user.Scripts; } public string Name { get; set; } // ... }
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"));
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());