Um von neuen Funktionen, Verbesserungen und Sicherheitsupdates zu profitieren ist es entscheidend, eingesetzte Software aktuell zu halten. Im letzten Artikel konnten Sie erfahren, warum es wichtig ist die Unterschiede zwischen .NET und .NET-Framework zu kennen, was die Kompatibilitätsprüfung ist und wie das grobe Vorgehen für ein Update ist. Dieses Mal wollen wir Ihnen technisch tiefere Einblicke geben.
Migration des Projektformats
Der erste Schritt sollte es sein, dass Sie auf das neue Standard-Projektformat wechseln, sofern Sie das noch nicht getan haben. Woran erkenne ich, welches Projektformat eingesetzt wird? Ein Indikator dafür, dass nicht das Standardformat genutzt wird, ist die „packages.config“ . Diese wurde bisher genutzt, um Referenzen auf Nuget Pakete zu verwalten. Im Standard-Projektformat werden die Paketreferenzen direkt in der .csproj Datei gepflegt.
.csproj ohne Standard-Projektformat:
<Project ToolsVersion="12.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> <Import Project="..\packages\Microsoft.Net.Compilers.2.3.2\build\Microsoft.Net.Compilers.props" Condition="Exists('..\packages\Microsoft.Net.Compilers.2.3.2\build\Microsoft.Net.Compilers.props')" /> ... <PropertyGroup> <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform> </PropertyGroup> ... </Project>
.csproj mit Standard-Projektformat:
<Project Sdk="Microsoft.NET.Sdk"> <PropertyGroup> <TargetFramework>net452</TargetFramework> <Authors>authorname</Authors> <PackageId>mypackageid</PackageId> <Company>mycompanyname</Company> </PropertyGroup> </Project>
Es gibt unterschiedliche Wege, um das Format zu migrieren. Zum einen gibt es Tools wie z.B. CsprojToVs2017 oder Eine weitere Möglichkeit besteht darin, die Solution neu zu erstellen und Ihr nach und nach die benötigten Referenzen und Dateien hinzuzufügen. Vorteile dieser Methode: es wird mit einem sauberen Stand begonnen und das Risiko des Übernehmens von Legacy-Code ist deutlich geringer. Mit diesem Schritt wird der Grundstein für die weitere Migration gelegt.
Migrieren der Bibliotheken auf .NET Standard
Als nächstes sollten intern geschriebene Bibliotheken auf das Taget Framework .NET Standard 2.0 umgestellt werden. Es ist der kleinste gemeinsame Nenner der unterschiedlichen Frameworks von Microsoft und somit kann Code, der auf .NET Standard 2.0 gebaut ist, sowohl mit dem .NET Framework als auch mit .NET ausgeführt werden.
Aufbau des .NET-Stacks. Quelle: Introducing .NET Standard – .NET Blog (microsoft.com)
Bei dieser Umstellung müssen ggf. erste Drittanbieterpakete ersetzt oder geupdatet werden. Die Erfahrung zeigt, dass oft Pakete betroffen sind, die etwas mit Datenbanken zu tun haben oder veraltete Protokolle verwenden. Es gibt hier die Möglichkeit, mittels der Nuget Website zu prüfen, welche Pakete migriert werden müssen. Alternativ kann der .NET Portability Analyzer verwendet werden, um herauszufinden welche Versionen unterstützt werden. Sollte kein entsprechendes Paket verfügbar sein, muss nach einer Alternative gesucht werden. Das benötigt meist etwas mehr Zeit und bringt einen größeren Umbau mit sich.
Um sicherzustellen, dass sich die Anwendung auch nach der Änderung weiterhin korrekt verhält, ist es wichtig, automatisierte Tests zu haben. Beispielsweise bei der Nutzung von Verschlüsselungsverfahren wurde festgestellt, dass Breaking Changes zwischen .NET-Framework und .NET auftreten können. Der folgende Codeausschnitt zeigt ein Beispiel aus unserem Projektalltag: Ein Breaking Change wurde eingeführt, bei dem es sich um „Partial and zero-byte reads in DeflateStream, GZipStream, and CryptoStream“ handelte. Nun muss beim Lesen des Crypto Streams folgendes angepasst werden:
von:
using (var cryptoStream = new CryptoStream(memoryStream, decryptor, CryptoStreamMode.Read)) { ... var decryptedByteCount = cryptoStream.Read(plainTextBytes, 0, plainTextBytes.Length); ... }
zu:
using (var cryptoStream = new CryptoStream(memoryStream, decryptor, CryptoStreamMode.Read)) { using (var streamReader = new StreamReader((Stream)cryptoStream)) { ... return streamReader.ReadToEnd(); ... } }
Migrieren der Executables
Nachdem alle Bibliotheken auf .NET Standard migriert wurden, haben wir die Basis geschaffen, um im nächsten Schritt die ausführbaren Projekte (Konsolen, ASP.NET Applikationen) umzustellen. Als Beispiel betrachten wir eine ASP.NET Anwendung. Es gilt insbesondere auf folgendes zu achten (weitere Details siehe Microsoft Learning):
- Umstellung der Dependency Injection von z.b. Unity auf Service Collection von .NET
- Implementieren der Middleware von ASP.NET Core
- Lesen von Applikationsparametern, nutzen von appsetting.json und `IConfiguration`
- Die Controller erben nicht mehr von `ApiController` sondern von `ControllerBase`. Der Rückgabewert ändert sich dadurch von `IHttpActionResult` auf `IActionResult`
Dies ist nur eine kleine Liste mit wichtigen Änderungen. Eine umfangreiche Übersicht für Breaking Changes zwischen den Versionen können Sie der Microsoft-Dokumentation MSDN entnehmen. Außerdem kann auch das .NET GitHub Repository verwendet werden, um offene und geschlossene Issues einzusehen und Hilfe für mögliche Probleme beim Upgrade-Prozess zu erhalten.
Anpassungen und Refactoring
Nach dem Upgrade der Lösung sind sehr wahrscheinlich einige Anpassungen des Codes erforderlich. Dies kann folgende Punkte umfassen:
- die Aktualisierung von veralteten APIs
- die Anpassung von Code, der nicht mit der geplanten .NET-Version kompatibel ist
Führen Sie diese Anpassungen schrittweise durch und überprüfen Sie den Code gründlich.
Ein paar Besonderheiten, auf die es zu achten gilt:
- WPF-Anwendungen können mit minimalen Änderungen auf .NET migriert werden. Guides dazu sind hier und hier zu finden.
- Für bestimmte Applikationen kann es hilfreich sein auf das Windows Compatibility Pack zu wechseln. Details dazu finden Sie
- Sollte Ihre App noch WCF verwenden, ist es von Vorteil auf das neue gRPC umzusteigen. Offiziell unterstützt .NET zwar den Aufruf von WCF-Services, jedoch gibt es keinen Support für das serverseitige Hosten von WCF. Eine Alternative ist der Umstieg auf die Open Source Variante CoreWCF. Wir empfehlen jedoch das Migrieren zu gRPC, da dies offiziell unterstützt wird, neueste Protokolle verwendet und schnellere Serialisierung und Deserialisierung bietet. Genaueres können sie unter diesem Link
Unsere Erfahrung hat gezeigt, dass in einer Migration am besten keine Warnungen des Compilers gelöst werden, sondern man sich ausschließlich auf das Upgrade des Paketes konzentrieren sollte. Mit schnellen Änderungen und Quick-Fixes schleichen sich schnell Fehler ein und es entsteht unter Umständen schlecht wartbarer Code. Wir empfehlen daher die Warnungen in einem weiteren Durchgang zu lösen.
Testen und Validieren
Nach dem Upgrade der Applikation inklusive aller Abhängigkeiten ist gründliches Testen entscheidend, um sicherzustellen, dass die Funktionalität der Anwendung nach dem Upgrade erhalten bleibt. Hier sind automatisierte Tests sehr wichtig, um Fehler schnell zu erkennen und zu beheben. Die Anwendung sollte außerdem unter verschiedenen Szenarien und Umgebungen manuell von mehreren Personen getestet und gesichtet werden, um die Zuverlässigkeit zu gewährleisten. Auch Performance-Tests können helfen, um sicherzustellen, dass die Anwendung mindestens noch so gut performt wie vor dem Upgrade.
Migration der Testframeworks
Die Test-Frameworks xUnit.net – GitHub, Selenium – GitHub und Microsoft Playwright – GitHub
Wird eine MSTest.TestFramework-Version kleiner 2.0.0 verwendet, so muss auch diese noch auf mindestens 2.2.5 geupdatet werden. Die Migration dazu ist von Microsoft hier gut dokumentiert. Bei anderen Test-Frameworks zeigt sich ein ähnliches Bild: entweder wird .NET 6 schon unterstützt oder es muss ein Update gemacht werden, um eine kompatible Version zu dem Framework zu erhalten. Sollte eine Migration notwendig sein, existiert meist eine entsprechende Dokumentation, wie z.B. von xUnit. Dies gilt auch für UI Test Frameworks wie z.B. Selenium (migration). Bei Playwright ist die Migration nicht notwendig, da dieses Framework auf .NET Standard 2.0 basiert und somit mit .NET Framework und .NET 6 kompatibel ist.
Statische Code Analyse
Statische Code Analyse sollte in jedem Softwareprojekt vorhanden sein und auch hier kann eine Migration der verwendeten, statischen Code Analyzer notwendig sein. Durch das Update der .NET-Version ändert sich die Runtime und es kann sein, dass Codeelemente anders ausgeführt werden. Wird in dem Projekt z.B. FxCop verwendet, so muss dieser migriert werden. Dabei ist es ratsam, sich am Migrations Guide zu orientieren. Es ist notwendig, diese Analyzer zu aktualisieren, da die unterschiedlichen Runtimes verschiedene Standard-Verhalten bei Ausführung von Code haben können. Ein sehr bekanntes ist beispielsweise, dass bei einer asynchronen Methode in den meisten Backend-Anwendungen eine „.ConfigureAwait(false)“ benötigt wird. In .NET 6 wird das nicht mehr benötigt.
Schulung und Unterstützung
Nach dem Upgrade ist es wichtig alle Teammitglieder mit der neuesten .NET-Version vertraut zu machen und informiert zu sein, wie sie die neuen Funktionen effektiv nutzen können und welche Best Practices eingehalten werden sollten. In diesem Rahmen ist es sinnvoll den Wissensaustausch innerhalb des Teams zu fördern und sich über aktuelle Entwicklungen auf dem Laufenden zu halten.
Sollten mit dem Upgrade größere Änderungen einhergehen, die sich auch funktional auf Ihre Anwendung auswirken, so sollten zuerst alle Anwender über die Änderungen informiert werden, bevor das Upgrade durchgeführt wird. Je nach Umfang der Anpassungen kann dies z.B. mittels interaktiver Formate (Webinare, Videos) oder schlank über eine kurze Dokumentation oder einem Gespräch erfolgen.
Fazit
Die Aktualisierung von mit dem .NET-Framework entwickelter Software auf die neueste .NET-Version ermöglicht es Ihnen von neuesten Sicherheitsupdates, modernen Funktionen, Leistungsoptimierungen und verbesserter Skalierbarkeit zu profitieren. Mit diesem zweiten Teil haben Sie einen tieferen Einblick in den Upgrade-Prozess erhalten und wichtige Schritte zur erfolgreichen Migration kennengelernt.
Eine gründliche Vorbereitung, Kompatibilitätsprüfung, Refactoring, umfangreiches Testen und ein sorgfältig geplanter Rollout sind entscheidend, um ein reibungsloses Upgrade zu gewährleisten. Schulungen und kontinuierliche Unterstützung des Entwicklerteams sind wichtig, um das volle Potenzial der neuen .NET-Version auszuschöpfen und Ihre Software auf dem neuesten Stand zu halten.
Oft wird die Modernisierung einer Lösung auf eine neue .NET-Version, aufgrund knapper Entwicklungskapazitäten, auf die lange Bank geschoben. Wird das Thema dann angegangen, weil es nicht mehr aufschiebbar ist, steigt schnell die Komplexität und es entsteht unnötiger Zeitdruck. Wie Sie diese Herausforderung umgehen und wie wir Sie z.B. mit einem agilen Team in unserem Managed-Nearshore-Modell bei dem Projekt unterstützen können lesen sie hier: Herausforderungen der Anwendungs-Modernisierung meistern – AIT GmbH & Co. KG