Häufig ist es so, dass wir abhängige Klassen erst zur Laufzeit aus dem Dependency Injection Container auflösen können. Vor vielen Jahren bin ich mit dem Microsoft Unity Framework gestartet. Da gab es nur eine Möglichkeit diese Auflösung hinzubekommen, und zwar über eine Factory Klasse. Das heißt, für alle Typen, die ich zur Laufzeit auflösen wollte habe ich dann eine Factory Klasse mit meistens einer Methode GetInstance(…) geschrieben. Viel Boilerplate Code also.
Und so sah das dann meistens aus:
Eine Klasse, die eine andere Klasse zur Laufzeit über die Factory Klasse auflöst.
namespace DependencyInjection;
public class BuildTimeDependency(IRuntimeDependencyFactory factory) : IBuildTimeDependency
{
private readonly string _runtimeDeterminationValue = „Ermittlung der information zur laufzeit und übergabe an die factory“;
public string GetMessage() => „Hello from BuildTimeDependency“ + factory.CreateInstance(_runtimeDeterminationValue);
}
public interface IBuildTimeDependency
{
string GetMessage();
}
In der Klasse BuildTimeDependency wurde eine oder mehrere Informationen ermittelt, die an eine andere Klasse, in meinem Fall die RuntimeDependency Klasse, weitergeleitet wurden, um arbeiten zu können. In meinem Beispiel leite ich die Information _runtimeDeterminationValue an die Factory weiter, die dann die abhängige Klasse erzeugt.
Die Factory sieht dann entsprechend so aus:
namespace DependencyInjection;
public class RuntimeDependencyFactory : IRuntimeDependencyFactory
{
public IRuntimeDependency CreateInstance(string runtimeDeterminationValue)
{
return new RuntimeDependency(runtimeDeterminationValue);
}
}
public interface IRuntimeDependencyFactory
{
IRuntimeDependency CreateInstance(string runtimeDeterminationValue);
}
Die zu erzeugende Klasse sieht so aus:
namespace DependencyInjection;
public class RuntimeDependency(string runtimeDeterminationValue) : IRuntimeDependency
{
public string GetMessage() => „Hello from RuntimeDependency“ + runtimeDeterminationValue;
}
public interface IRuntimeDependency
{
string GetMessage();
}
Häufig wurde im Konstruktur noch der IServiceProvider () Type übergeben, um nicht new RuntimeDependency(…), sondern Resolve
Mit dem Package Microsoft.Extension.Hosting und der alten Factory Variante sieht das dann so aus (Program.cs):
using DependencyInjection;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
var host = Host.CreateDefaultBuilder(args)
.ConfigureServices((_, services) =>
{
services.AddTransient
services.AddTransient
})
.Build();
var instance = host.Services.GetService
Console.WriteLine(instance?.GetMessage());
Mit diesem neuen Packages sind wir in der Lage, diese Factories zu entfernen. Die Factory Klassen können wir durch gegistrierte Func<…> ersetzen. Das sieht dann wie folgt aus:
Dort wo die Factory Klasse hineingegeben wurde setzen wir eine Func
namespace DependencyInjection;
public class BuildTimeDependency(Func
{
private readonly string _runtimeDeterminationValue = „Ermittlung der information zur laufzeit und übergabe an die factory“;
public string GetMessage() => „Hello from BuildTimeDependency“ + factory(_runtimeDeterminationValue);
}
public interface IBuildTimeDependency
{
string GetMessage();
}
Diese Func müssen wir dann nur noch dem Hosting bekanntmachen. Das sieht dann so aus:
services.AddTransient
Das p (ServiceProvider) können wir in meinem Fall discarden (discard param). Dieser wird nur benötigt, wenn wir eine Mischform haben. Das bedeutet, wenn die Klasse RuntimeDependency weitere abhängige Klassen hat, die aufgelöst werden müssen und zur Buildzeit feststehen. Dann würde man p.GetService oder p.GetKeyedService p.GetRequieredService etc. aufrufen, um die Abhängigkeiten aufzulösen.
Fazit: Eigene Factory Klassen zum Auflösen von Abhängigkeiten sind mit dem Package Microsoft.Extension.Hosting (Microsoft.Extension.DependencyInjection) nicht mehr notwendig. Diese können durch Factory Methoden (Func<…>) ersetzt werden.