Es ist eine gute Idee, die Entscheidung, eine Art Werkzeug oder Bibliothek zu verwenden, bis the last responsible moment zu verschieben. Mit einem guten Design können Sie später eine DI-Bibliothek hinzufügen. Dies bedeutet, dass Sie Pure DI üben.
Der bevorzugte Interceptionspunkt in MVC ist die IControllerFactory
Abstraktion, da es Ihnen ermöglicht, die Erstellung von MVC-Controllern abzufangen, und dies verhindert, dass Sie einen zweiten Konstruktor implementieren müssen (is an anti-pattern). Obwohl es möglich ist IDependencyResolver
, die Verwendung dieser Abstraktion zu verwenden ist viel weniger praktisch, weil es auch von MVC zu lösen Dinge genannt wird man in der Regel nicht daran interessiert ist.
Ein benutzerdefinierte IControllerFactory
, die als composition root handeln kann umgesetzt werden wie folgt:
public sealed class CompositionRoot : DefaultControllerFactory
{
private static string connectionString =
ConfigurationManager.ConnectionStrings["app"].ConnectionString;
private static Func<BooksContext> bookContextProvider = GetCurrentBooksContext;
private static IBookRepository bookRepo = new BookRepository(bookContextProvider);
private static IOrderBookHandler orderBookHandler = new OrderBookHandler(bookRepo);
protected override IController GetControllerInstance(RequestContext _, Type type) {
// Unfortunately, because of MVC's design, controllers are not stateless, and
// you will have to create them per request.
if (type == typeof(OrderBookController))
return new HomeController(orderBookHandler);
if (type == typeof(BooksController))
return new BooksController(bookRepo);
// [other controllers here]
return base.GetControllerInstance(_, type);
}
private static BooksContext GetCurrentBooksContext() {
return GetRequestItem<BooksContext>(() => new BooksContext(connectionString));
}
private static T GetRequestItem<T>(Func<T> valueFactory) where T : class {
var context = HttpContext.Current;
if (context == null) throw new InvalidOperationException("No web request.");
var val = (T)context.Items[typeof(T).Name];
if (val == null) context.Items[typeof(T).Name] = val = valueFactory();
return val;
}
}
kann Ihre neue Controller Fabrik in MVC angeschlossen werden, wie folgt:
public class MvcApplication : System.Web.HttpApplication
{
protected void Application_Start() {
ControllerBuilder.Current.SetControllerFactory(new CompositionRoot());
// the usual stuff here
}
}
Wenn Sie reine DI üben, werden Sie in der Regel Ihre composit sehen Ionenwurzel bestehen aus einer großen Liste von if
Aussagen. Eine Anweisung pro Stammobjekt in Ihrer Anwendung.
Der Anfang mit Pure DI hat einige interessante Vorteile. Am bekanntesten ist die Kompilierzeit-Unterstützung, da Sie diese sofort verlieren werden, wenn Sie eine DI-Bibliothek verwenden. Einige Bibliotheken versuchen, diesen Verlust zu minimieren, indem Sie Ihre Konfiguration auf eine Weise verifizieren, die der Compiler durchführen würde. aber diese Überprüfung wird zur Laufzeit durchgeführt und der Rückkopplungszyklus ist nie so kurz wie der, den der Compiler Ihnen geben kann.
Bitte seien Sie nicht versucht, die Entwicklung zu vereinfachen, indem Sie einen Mechanismus implementieren, der das Erstellen von Reflektionstypen ermöglicht, da Sie damit Ihre eigene DI-Bibliothek erstellen. Es gibt viele Nachteile, z.B. Sie verlieren die Kompilierzeit-Unterstützung, ohne die Vorteile einer vorhandenen DI-Bibliothek zu erhalten.
Wenn Ihr Kompositionswurzel schwer zu warten wird, sollten Sie in diesem Moment den Wechsel von Pure DI in eine DI-Bibliothek in Betracht ziehen.
tun, dass in meiner Beispiel Zusammensetzung Wurzel zu beachten, alle Anwendungskomponenten (mit Ausnahme des Controller) als Singleton definiert. Singleton bedeutet, dass die Anwendung nur eine Instanz jeder Komponente hat. Bei diesem Design müssen Ihre Komponenten zustandslos (und damit threadsicher) sein, alles, was einen Status hat (z. B. BooksContext
) should not be injected through the constructor. Im Beispiel habe ich einen Func<T>
als Provider des BooksContext
verwendet, der pro Anfrage gespeichert wird.
Die Erstellung von Objektgraphen, Singletons, bietet viele interessante Vorteile. Zum Beispiel verhindert es häufige Konfigurationsfehler wie Captive Dependencies und zwingt Sie in ein solideres Design. Darüber hinaus sind einige DI-Bibliotheken sehr langsam, und alles, was ein Singleton ist, könnte Performance-Probleme beim späteren Wechsel zu einer DI-Bibliothek verhindern. Auf der anderen Seite ist der Nachteil dieses Entwurfs, dass jeder im Team verstehen sollte, dass alle Komponenten zustandslos sein müssen. Das Speichern des Zustands in Komponenten führt zu unnötigem Kummer und Ärger. Nach meiner Erfahrung sind zustandsbehaftete Komponenten wesentlich leichter zu erkennen als die meisten DI-Konfigurationsfehler.Ich habe auch bemerkt, dass Singleton-Komponenten etwas sind, das für die meisten Entwickler natürlich ist, besonders für jene, die keine Erfahrung mit DI haben.
Beachten Sie, dass ich im Beispiel manuell eine pro-Anforderung Lebensstil für die BooksContext
implementiert. Obwohl alle DI-Bibliotheken Out-of-the-Box-Unterstützung für bestimmte Lebensstile wie Lebensläufe pro Anfrage bieten, würde ich mich dagegen aussprechen, diese eingeschränkten Lebensstile zu verwenden (außer vielleicht, wenn die Bibliothek sicherstellt, dass eine Ausnahme ausgelöst wird). Die meisten Bibliotheken warnen Sie nicht, wenn Sie eine Bereichsinstanz außerhalb des Kontexts eines aktiven Bereichs auflösen (z. B. wenn Sie eine pro-Request-Instanz in einem Hintergrundthread auflösen). Einige Container geben Ihnen eine Singleton-Instanz zurück, andere geben Ihnen bei jeder Anfrage eine neue Instanz zurück. Das ist wirklich lästig, weil es Bugs versteckt und Sie viele Stunden lang versuchen lässt, Ihre Anwendung zu debuggen (ich spreche aus Erfahrung hier).
Ich würde SimpleInjector empfehlen, da es die Konfiguration vor dem Start überprüft. Sehr wenige Container machen das. So würde jedes Problem in der Regel am Anfang gefunden und gelöst werden. – jgauffin
Wenn Sie irgendwann planen, Ihr Projekt auf ASP.NET vNext zu aktualisieren, ist es erwähnenswert, dass einige grundlegende Implementierungen von DI-Containern enthalten sind, die Ihren Anforderungen entsprechen (http://blogs.msdn.com/b /webdev/archive/2014/06/17/dependency-injection-in-asp-net-vnext.aspx) –
Ich denke, dass es viel besser wäre, mit Pure DI zu beginnen, anstatt ASP.NET vNext eingebauten DI zu verwenden Bibliothek. [Die integrierte DI-Bibliothek ist nutzlos, wenn Sie SOLID-Anwendungen schreiben] (https://stackoverflow.com/a/30682214/264697). – Steven