Kolumne: .NETversum

Geschlüsselte Dienste bei der Dependency Injection in .NET 8.0

Geschlüsselte Dienste bei der Dependency Injection in .NET 8.0

Kolumne: .NETversum

Geschlüsselte Dienste bei der Dependency Injection in .NET 8.0


.NET 8.0 bietet bei dem im modernen .NET integrierten Dependency-Injection-Container eine neue Funktion, bei der man Schlüssel bei Befüllen des Containers angeben kann und einzelne Objekte dann anhand des Schlüssels konsumieren kann.

Die Keyed Dependency Injection Services sind neu in .NET 8.0 [1]. Hierbei kann bei der Konfiguration des Dependency-Injection-Containers ein zusätzliches Objekt angegeben werden, auf den Nutzer:innen Bezug nehmen müssen.

Ein Schlüssel wird verwendet, um diesen Singleton-Dienst eindeutig zu identifizieren. Das ist besonders nützlich, wenn Sie mehrere Implementierungen desselben Diensts registrieren möchten, aber jede mit einem anderen Schlüssel versehen, um sie später entsprechend abzurufen.

Dazu gibt es drei neue Methoden in .NET 8.0 zum Befüllen des Dependency-Injection-Containers:

AddKeyedSingleton()
AddKeyedScoped()
AddKeyedTransient()

Beispiel: AddKeyedSingleton()

Das wird am Beispiel eines Singleton-Diensts nun genauer erklärt. Beim Befüllen des Containers verwendet man: services.AddKeyedSingleton<Klasse>(Schlüssel); oder services.AddKeyedSingleton<Schnittstelle, Klasse>(Schlüssel);. Mit [FromKeyedServices("remote")] kann eine Instanz im Dependency-Injection-Container im Konstruktor eine geschlüsselte Instanz einer anderen Klasse anfordern. Imperativ geht das per serviceProvider.GetKeyedService<T>(Schlüssel). Bei einem geschlüsselten Dienst funktioniert das bisher schon verfügbare serviceProvider.GetRequiredService<T>() nicht.

Listing 1 zeigt Zeichenketten als Schlüssel zur Unterscheidung zwischen zwei verschiedenen Implementierungen einer Schnittstelle mit Namen IDataProvider.

Listing 1: Einsatz von Keyed Dependency Injection Services

using ITVisions;
using Microsoft.Extensions.DependencyInjection;
 
namespace NET8_Console;
 
interface IDataProvider
{
  public string Name { get; set; }
  public List<Object> GetData();
}

class LocalData : IDataProvider
{
  public string Name { get; set; } = "Lokale Daten";
  public List<Object> GetData()
  {
    return new List<Object>(); // irgendwas Lokales }
  }
}
 
class RemoteData : IDataProvider
{
  public string Name { get; set; } = "Daten von WebAPI";
  public List<Object> GetData()
  {
    return new List<Object>(); // irgendwas vom WebAPI }
 
  }
}
 
class LocalDataConsumer([FromKeyedServices("local")] IDataProvider data)
{
  public override string ToString()
  {
    return data.Name + ": " + data.GetData().Count + " Datensätze!";
  }
}
 
class RemoteDataConsumer([FromKeyedServices("remote")] IDataProvider data)
{
  public override...