Dependency Injection Container für C# (.NET): vollständige Anleitung
Dependency Injection (DI) ist eines der Schlüsselprinzipien der modernen Entwicklung mit C# (.NET), das es ermöglicht, lose gekoppelte, testbare und leicht wartbare Anwendungen zu erstellen. Im .NET-Ökosystem ist das wichtigste Werkzeug zur Implementierung von DI der integrierte Container aus dem Paket Microsoft.Extensions.DependencyInjection. In diesem Artikel werden wir im Detail besprechen, was ein DI-Container ist, wie man ihn konfiguriert, welche Lebenszyklen es gibt und wie man ihn in realen Projekten richtig einsetzt.
Was ist ein Dependency Injection Container?
Ein Dependency Injection Container (oder IoC-Container) ist eine Softwarekomponente, die automatisch die Erstellung und Injektion von Objektabhängigkeiten verwaltet. Anstatt Objekte manuell mit new zu erstellen, registrieren Sie Typen im Container, und dieser kümmert sich um deren Erstellung und Verknüpfung.
Die Hauptvorteile der Verwendung eines DI-Containers:
- Reduzierung der Codekopplung — Klassen hängen von Abstraktionen (Schnittstellen) ab, nicht von konkreten Implementierungen.
- Vereinfachung des Testens — Abhängigkeiten können leicht durch Mock-Objekte ersetzt werden.
- Zentrale Lebenszyklusverwaltung — Sie kontrollieren, wann Objekte erstellt und zerstört werden.
- Erhöhte Flexibilität — Implementierungen können geändert werden, ohne den Code der Verbraucher zu ändern.
Installation und Konfiguration des DI-Containers
In modernen .NET-Projekten (ASP.NET Core, Console, Worker Services) ist der DI-Container bereits integriert. Für die Nutzung reicht es, das Paket zum Projekt hinzuzufügen:
dotnet add package Microsoft.Extensions.DependencyInjectionDanach wird eine Instanz von IServiceCollection erstellt, in der alle Abhängigkeiten registriert werden. Betrachten wir ein einfaches Beispiel für die Registrierung von Diensten:
using Microsoft.Extensions.DependencyInjection;
// Schnittstelle und ihre Implementierung definierenpublic interface IMessageService{ void Send(string message);}
public class EmailService : IMessageService{ public void Send(string message) { Console.WriteLine($"Email sent: {message}"); }}
// Container konfigurierenvar services = new ServiceCollection();services.AddTransient<IMessageService, EmailService>();
// Service-Provider erstellenvar serviceProvider = services.BuildServiceProvider();
// Dienstinstanz abrufenvar messageService = serviceProvider.GetService<IMessageService>();messageService.Send("Hello from DI!");Lebenszyklen von Abhängigkeiten
Der DI-Container unterstützt drei Hauptmodi zur Verwaltung der Lebensdauer von Objekten:
- Transient (AddTransient) — jede Anforderung erstellt eine neue Instanz. Geeignet für zustandslose, leichtgewichtige Dienste.
- Scoped (AddScoped) — eine Instanz pro Bereich (z. B. pro HTTP-Anfrage in ASP.NET Core). Ideal für Dienste mit Zustand innerhalb eines Vorgangs.
- Singleton (AddSingleton) — eine einzige Instanz für die gesamte Anwendung. Wird für Caches, Logger, Konfigurationen verwendet.
Beispiel für die Registrierung mit verschiedenen Lebenszyklen:
services.AddTransient<ITransientService, TransientService>();services.AddScoped<IScopedService, ScopedService>();services.AddSingleton<ISingletonService, SingletonService>();Wichtig: Wenn ein Dienst von einem anderen Dienst mit kürzerem Lebenszyklus abhängt, kann dies zu Fehlern führen (z. B. ein Singleton, der einen Scoped-Dienst verwendet). Solche Situationen werden als "captive dependencies" bezeichnet und sollten vermieden werden.
Abhängigkeitsinjektion über den Konstruktor
Die gebräuchlichste Methode der Injektion ist über den Konstruktor. Der Container löst automatisch alle Abhängigkeiten auf
Auch in der Bibliothek
Prometheus-Metriken in Kotlin: Integration mit Micrometer und Ktor
Bytes für Rust: Vollständige Anleitung mit Beispielen
Generierung von PDF-Berichten in Kotlin: iText, Apache PDFBox, OpenPDF