.NET Core kommt mit einer integrierten Dependency Injection. Dadurch kann man sich fertige Instanzen registrierter Typen/Schnittstellen an beliebigen Stellen „einfügen“ (oder injecten) lassen.

Dependency Injection ist ein Entwurfsmuster. Dieses definiert die Abhängigkeiten eines Objektes zur Laufzeit. Alle Abhängigkeiten werden an einem zentralen Ort registriert (vgl. Container) und daraus bezogen (vgl. Single Responsibility). Das Objekt ist also nicht mehr verantwortlich, auch alle Abhängigkeiten selbst zu erstellen.

Ausgangssituation

In der Realität gibt es viele Typen, die eine Konfiguration benötigen. Seien es Klassen, die für die Arbeit mit Datenbanken geschrieben wurden und Verbindungsinformationen benötigen, oder Logger, die eine Konfiguration der Verzeichnisse, Log-Level etc. erfordern.

Im Zuge dieses Beitrags wird eine Klasse SendMailService erstellt, welche Informationen aus der Datei appsettings.json benötigt. Diese Einstellungen sollen automatisch via Dependency Injection bezogen werden. Dies vermeidet das Durschleifen der Konfiguration über Konstruktor-Parameter oder Properties oder das halten der Informationen in statischen Klassen oder Singletons.

Konfiguration

Gegeben ist folgende Konfiguration, die sich in der Datei appsettings.json befindet:

"MailSettings": {
    "RedirectAllMails": true,
    "RedirectTo": "hello@",
    "Server": "",
    "Port": 25,
    "User": "username",
    "Password": "password",
    "From": "sender@"
}

Dazu passend wird eine Klasse MailServiceOptions erstellt:

public class MailServiceOptions
{
    public bool RedirectAllMails { get; set; }
    public string RedirectTo { get; set; }
    public string Server { get; set; }
    public int Port { get; set; }
    public string User { get; set; }
    public string Password { get; set; }
    public string From { get; set; }
}

Damit die Optionen bereit gestellt werden können, muss alles Notwendige für die Arbeit mit Optionen aktiviert werden. Dazu ist in der Startup.cs in der ConfigureServices die folgende Zeile Code hinzuzufügen:

services.AddOptions();

Als nächster Schritt wird die Klasse MailServiceOptions registriert und definiert, woher die Informationen dazu kommen:

services.Configure<MailServiceOptions>(Configuration.GetSection("MailSettings"));

Der letzte Schritt besteht darin, sich die Optionen ins Service injecten zu lassen. Dazu steht das Interface IOption zur Verfügung:

public class SendMailService
{
    private MailServiceOptions MailOptions { get; set; }

    public SendMailService(IOptions<MailServiceOptions> options)
    {
        MailOptions = options.Value;
    }
}

Die Registration erfolgt in der Startup.cs in der Methode ConfigureServices. Soll der Typ SendMailService über Dependency Injection zur Verfügung stehen, ist dieser zu registrieren:

services.AddTransient(typeof(SendMailService));

In diesem Fall wird der genannte Typ als transientes Service registriert. Dies bedeutet, dass bei jeder Anforderung eine neue Instanz erstellt wird.

Ab sofort kann das Service an beliebigen Stellen samt Konfiguration injected werden.

Konfigurationen Auto-Reload

Besonders praktisch ist es, wenn die Applikation bei einer Änderung der Konfiguration nicht neu gestartet werden muss, sondern diese sofort übernommen werden.

Hierzu bedarf es einen Blick an die Stelle, welche die Konfiguration mit Hilfe des ConfigurationBuilder erstellt. Das wird häufig im Konstruktor der Klasse Startup.cs gemacht und sieht in etwa so aus:

var builder = new ConfigurationBuilder()
    .SetBasePath(env.ContentRootPath)
    .AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)
    .AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true)
    .AddEnvironmentVariables();
Configuration = builder.Build();

Besonderes Augenmerk verdient die Einstellung reloadOnChange. Ist diese auf true, wird die Konfiguration bei jeder Änderung nachgeladen.

Soll unser Service nun davon profitieren, ist lediglich das Interface IOption gegen IOptionsSnapshot zu tauschen. Schon wird beim Erzeugen einer neuen Instanz die neue Konfiguration geladen.

Wird ein Service beispielsweise via AddSingleton registriert, funktioniert dies nicht, da insgesamt nur eine einzige Instanz des Typs existiert und die Konfiguration zum Zeitpunkt der Instanzierung herangezogen wird.

Fazit

Zum Thema Dependency Injection gibt es viel zu erzählen. Dieser Beitrag kratzt nur an der Oberfläche und setzt einschlägiges Wissen voraus. Es wird gezeigt, wie einfach Konfigurationen aus Konfigurationsdateien per Dependency Injection verwendet werden können. Zusätzlich wird eine Möglichkeit geboten, wie diese zur Laufzeit aktualisiert werden können.

Happy Coding!

Über den Autor

Norbert Eder

Ich bin ein leidenschaftlicher Softwareentwickler und Fotograf. Mein Wissen und meine Gedanken teile ich nicht nur hier im Blog, sondern auch in Fachartikeln und Büchern.

Hinterlasse einen Kommentar