Die kurze Frage ist: Gegeben eine Bibliothek garantiert die Verwendung eines bestimmten IOC - Containers für seine Interna, wenn eine Anwendung diese Bibliothek nutzt, vorausgesetzt, die App garantiert einen IOC - Container für die Verdrahtung ihrer Abhängigkeiten zu verwenden die beiden Behälter sind anders, wie können sie gut zusammenspielen?
Das Szenario besteht darin, dass in der Anwendung Klassen definiert sind, die von Typen aus der Bibliothek abhängen. Wenn der Anwendungscontainer versucht, eine solche Klasse zu erstellen, muss er wissen, wie der in der Bibliothek vorhandene Typ aufgelöst werden kann.
Hier ist die langatmige Frage:
Diese Frage scheint in verschiedenen Formen und Formen zuvor schon auf SO gestellt worden zu sein, aber ich kann nicht die Antwort finden, die ich brauche, also werde ich es mit einem hypothetischen _over_simplified_ konkreten Beispiel versuchen. Wir möchten eine Bibliothek für die Protokollierung schreiben, die Benutzer als Paket in ihre Lösung aufnehmen können, um die Protokollfunktionalität sofort zu nutzen. Die öffentlichen Schnittstellen, über die die Bibliothek verfügt, sind ..
public interface ILogger {}
public interface ITarget {}
Konkrete Implementierungen sind
internal class Logger: ILogger { public Logger(ITarget target) {}}
internal class FileTarget : ITarget {}
Wenn der Benutzer unser Paket enthält und eine Klasse mit einer Eigenschaft vom Typ ILogger
definiert oder ein ctor-Argument vom Typ ILogger
hat, dann ist unsere Bibliothek dafür verantwortlich, eine konkrete Implementierung für diese Schnittstelle in den Benutzer einzufügen Klasse. Standardmäßig wird der injizierte Logger zum Logging an das Dateisystem weitergeleitet, da die Standardimplementierung von ITarget
, die in die ILogger
-Implementierung eingefügt wurde, ein FileTarget
von unserer Bibliothek ist.
Wenn der Benutzer beschließt, eine Klasse zu schreiben, die die ITarget
-Schnittstelle implementiert, wird unsere Bibliothek diese verwenden, um in die Logger
-Klasse zu injizieren und nicht die standardmäßige FileTarget
-Implementierung.
SO, was ich zeigen möchte, ist ihre bidirektionale Abhängigkeit hier.
Unsere Bibliothek hängt von den Assemblys des Benutzers ab, da sie die Assemblys des Benutzers scannen muss, um beliebige Erweiterungspunkte (dh eine ITarget
-Implementierung) zu laden und diese vor einer Standardimplementierung in ihre eigenen Objekte zu injizieren. p>
Die Assemblys des Benutzers hängen von der Bibliothek ab. Wenn der Benutzer eine Klasse mit einer ILogger
-Schnittstelle als Abhängigkeit definiert, sollte dieses Benutzerobjekt einen konkreten Verweis auf diese Schnittstelle erhalten, die zur Laufzeit von unserem bereitgestellt wird Bibliothek.
Die einfache Lösung ist, wenn der Benutzer und unsere Bibliothek beide denselben IOC-Container verwenden, dann ist das Problem gelöst. Aber das ist eine starke Annahme. Was ich tun möchte, ist
So weit so gut, es ist perfekt erreichbar, aber hier kommt der knifflige Teil.
Ich möchte fast eine Art von untergeordneten Container-Funktionalität in der Bibliothek mit einer Schnittstelle wie folgt
definieren public interface IDependencyResolvingModule { T Get<T>(); []T GetAll<T>(); }
und stellen eine Implementierung bereit, die den Container unserer Bibliothek (d. h. Ninect) verwendet, um den in den beiden oben definierten Methoden angeforderten Typ aufzulösen.
Ich möchte, dass der IOC-Container des Benutzers einige Funktionen hat. Wenn er eine Abhängigkeit nicht auflösen kann (d. h. ILogger
), sollte er sich in die IDependencyResolvingModule
-Implementierung einklinken und nach der Abhängigkeit fragen.
Auf diese Weise kann unsere Bibliothek den IOC-Container auswählen, und der Code des Benutzers kann Abhängigkeiten auflösen, von denen sein IOC-Container keine Ahnung hat. Funktioniert diese Lösung nicht, wenn IOC Container einige der bereitgestellten Funktionen zur Registrierung von Singleton-Instanzen von IDependencyResolverModules
, die in Assemblys im Verzeichnis der ausführenden Assembly gefunden wurden, und wenn sie einen Typ nicht auflösen können, eines der Singleton-Module fragt? / p>
Aber abgesehen von einer Lösung, die jeden anderen IOC-Container unterbringen muss, wie sonst kann das gelöst werden? Das Problem in ein paar Zeilen ist, wenn eine Third-Party-Assembly einen IOC-Container für seine Interna verwendet, was eine einfache Lösung ist, so dass diese Bibliothek einfach einen Mechanismus für einen IOC-Container bereitstellen kann, der sich in Abhängigkeiten einklinken und auflösen kann die in der Bibliothek leben.
Ich sehe hier einige mögliche Ansätze:
Schreiben Sie den Standard-Registrator für alle gängigen IoC-Container. Jeder von ihnen sollte in der separaten Baugruppe platziert werden. Dann kann der Entwickler den gewünschten auswählen und seinen Container damit konfigurieren.
Definieren Sie Ihre eigene Factory-Abstraktion und schreiben Sie eine Standardimplementierung, die den Standardlogger zurückgibt. Lassen Sie den Entwickler die Implementierung dieser Fabrik ersetzen. Zum Beispiel mit Adapter für seinen Lieblingscontainer. Dieser Ansatz ist am Container-Agnostisch, da der Entwickler einfach die Standard-Factory-Implementierung verwenden kann. Aber dieser Weg hat nichts mit Autoverdrahtung zu tun.
Die faule Variante des ersten Ansatzes. Schreiben Sie ein kleines Handbuch zum Konfigurieren eines Containers, um mit Standardimplementierungen zu arbeiten. Dann könnte der Entwickler den Container selbst konfigurieren.
Kombinieren Sie alle vorherigen Lösungen, um jeden Entwickler zufrieden zu stellen. :)
EDIT: hinzugefügtes Beispiel der Integration von zwei Containern
%Vor%Tags und Links c# dependency-injection ioc-container inversion-of-control