Migration von .Net Framework 4.8

Migration von .Net Framework 4.8 bis zur aktuellen Version .Net8

Michael Friedrichs | Softwareentwickler | 15.03.2024

Migration von .Net Framework 4.8 bis zur aktuellen Version .Net8 

Ausgehend von einem Projekt (hier ein asp.Net MVC Projekt im .Net Framework 4.8) wollen wir uns anschauen, wie eine spezielle Implementierung in nachfolgenden .Net-Versionen umgesetzt werden kann oder muss.

Aufgrund einer Anforderung ist es erforderlich, die Services im MVC-Controller dynamisch auszuwählen. In unserem Fall wird aufgrund eines ausgewählten Landes entschieden, welche Services verwendet werden müssen. In den Services sind jeweils unterschiedliche Import und Export-Methodiken implementiert.

 

.Net-Framework 4.8: Konstruktor mit Factory-Pattern  

public class ImportController : BaseController
{
    private ImportCSVService _ImportCSVService = null;
    private ExportCSVService _ExportCSVService = null;

    /// <summary>
    /// Standard-Konstruktor mit Initialisierung der Services (abhängig vom aktiven Land)
    /// </summary>
    public ImportController()
    {
        if (land == LandEnum.NL)
        {
            _ImportCSVService = new ImportViService();
            _ExportCSVService = new ExportViService();
        }
        else if (land == LandEnum.BE)
        {
            _ImportCSVService = new ImportV2Service();
            _ExportCSVService = new ExportV2Service();
        }
    }

    public ActionResult Index()
    {
        var model = _ImportCSVService.ImportCSV("test.csv");
        return View(model);
    }
}

So viel zur Ausgangssituation.

Schauen wir uns nun an, wie wir das ganze zu neueren .Net-Versionen portieren können

 

.Net 6 oder .Net 7: Konstrukt

services.AddScoped<IImportCSVService, ImportV1Service>();
    services.AddScoped<IExportCSVService, ExportV1Service>();
    services.AddScoped<IImportCSVService, ImportV2Service>();	// eine 2. Registrierung funktioniert nicht
    services.AddScoped<IExportCSVService, ExportV2Service>();

 

Spätestens seit .Net 5 verwenden wir generell Dependency Injection und instanziieren die Services nicht in der Klasse, sondern registrieren die Services mittels ihrer Interfaces und geben Sie dann im Konstruktor als Parameter mit.

public ImportNNController(IImportCSVService importV1Service, IExportCSVService exportV1Service,
IImportCSVService importV2Service, IExportCSVService exportV2Service)
{
if (landEnum == LandEnum.NL)
{
_ImportCSVService = importV1Service;
_ExportCSVService = exportV1Service;
}
else if (landEnum == LandEnum.BE)
{
_ImportCSVService = importV2Service;
_ExportCSVService = exportV2Service;
}
}

 

Doch wie registriert und übergibt man jetzt 2 unterschiedliche Services mit demselben Interface?

public interface IImportV1Service : IImportCSVService
{
}

Die einfachste Variante ist die Einführung von entsprechenden Hilfs-Interfaces, die eigentlich keine weitere Funktionalität haben, außer die Services auseinander halten zu können. Hier beispielhaft für eines der Interfaces:

Nun können wir die Services entsprechend registrieren…

services.AddScoped<IImportV1Service, ImportV1Service>();
    services.AddScoped<IExportV1Service, ExportV1Service>();
    services.AddScoped<IImportV2Service, ImportV2Service>();
    services.AddScoped<IExportV2Service, ExportV2Service>();

… und im Konstruktor verwenden.

public ImportNNController(IImportV1Service importV1Service, IExportV1Service exportV1Service,
                          IImportV2Service importV2Service, IExportV2Service exportV2Service)
{
    if (landEnum == LandEnum.NL)
    {
        _ImportCSVService = importV1Service;
        _ExportCSVService = exportV1Service;
    }
    else if (landEnum == LandEnum.BE)
    {
        _ImportCSVService = importV2Service;
        _ExportCSVService = exportV2Service;
    }
}

 

Aber eigentlich ist dies nur ein Workaround … Man könnte die Services natürlich auch per Reflektion dynamisch generieren – aber dann würden wir an der Dependency Injection vorbei implementieren.

 

.Net 8: Konstruktor mit Factory-Pattern

Mit .Net8 geht dies nun etwas einfacher. Das entsprechende Schlagwort an dieser Stelle ist ‚KeyedServices‘, welche mit dieser .Net-Version neu eingeführt wurden.

Die Services werden entsprechend etwas anders registriert:

builder.Services.AddKeyedScoped<IImportCSVService, ImportV1Service>("V1");
    builder.Services.AddKeyedScoped<IExportCSVService, ExportV1Service>("V1");
    builder.Services.AddKeyedScoped<IImportCSVService, ImportV2Service>("V2");
    builder.Services.AddKeyedScoped<IExportCSVService, ExportV2Service>("V2");

 

wir können hier die eigentlichen Basis-Interface verwenden

public ImportController([FromKeyedServices("V1")] IImportCSVService importV1Service, 
                        [FromKeyedServices("V1")] IExportCSVService exportV1Service, 
                        [FromKeyedServices("V2")] IImportCSVService importV2Service, 
                        [FromKeyedServices("V2")] IExportCSVService exportV2Service)
{
     if (land == LandEnum.NL)
     {
        _ImportCSVService = importV1Service;
        _ExportCSVService = exportV1Service;
     }
     else if (land == LandEnum.BE)
     {
        _ImportCSVService = importV2Service;
        _ExportCSVService = exportV2Service;
     }
}

 

builder.Services.AddKeyedScoped<IImportCSVService, ImportV1Service>(LandEnum.NL);
    builder.Services.AddKeyedScoped<IExportCSVService, ExportV1Service>(LandEnum.NL);
    builder.Services.AddKeyedScoped<IImportCSVService, ImportV2Service>(LandEnum.BE);
    builder.Services.AddKeyedScoped<IExportCSVService, ExportV2Service>(LandEnum.BE);
builder.Services.AddKeyedScoped<IImportCSVService, ImportV1Service>(LandEnum.NL);
    builder.Services.AddKeyedScoped<IExportCSVService, ExportV1Service>(LandEnum.NL);
    builder.Services.AddKeyedScoped<IImportCSVService, ImportV2Service>(LandEnum.BE);
    builder.Services.AddKeyedScoped<IExportCSVService, ExportV2Service>(LandEnum.BE);

Die Strings (‚V1‘ und ‚V2‘) sollten aber als Konstante definiert und verwendet werden. Hier könnten wir auch das Enum direkt verwenden, da als Key jedes Objekt verwendet werden kann.

public ImportController([FromKeyedServices(land)] IImportCSVService importService, 
                        [FromKeyedServices(land)] IExportCSVService exportService)
{
    _ImportCSVService = importService;
    _ExportCSVService = exportService;
}

 

Michael Friedrichs | Softwareentwickler | 15.03.2024

Sie suchen einen Software Anbieter?
Sprechen Sie uns an