Migration von .NET Framework 4.8 zu .NET 11 im Jahr 2026
Ein versionsgenaues Migrations-Playbook für die Umstellung einer .NET Framework 4.8-Codebasis auf .NET 11 LTS im Jahr 2026, einschließlich der Umstellung auf das SDK-Style csproj, System.Web zu ASP.NET Core, WCF, EF6 zu EF Core 11, BinaryFormatter-Entfernung, AppDomain-Ersatz und einem realistischen Rollback-Plan.
Die Umstellung einer .NET Framework 4.8-Codebasis auf .NET 11 ist kein bloßer Versionssprung. Es handelt sich um eine Re-Platforming-Übung, die das Projektformat, den Web-Stack, die Datenzugriffsschicht, das Hosting-Modell und einen langen Schwanz von APIs berührt, die zwischen 2017 und 2026 still und leise verschwunden sind. Das offizielle Servicing-Fenster für .NET Framework 4.8 ist 2026 noch offen, aber die Laufzeit hat seit 2019 kein Funktions-Update mehr erhalten, jeder moderne Azure App Service-Plan läuft standardmäßig auf dem Core-Stack, und nahezu jedes NuGet-Paket, von dem man abhängen sollte, hat seine net48-Targets fallen gelassen. Der realistische Aufwand für eine typische Line-of-Business-Anwendung beträgt zwei bis sechs Wochen für einen kleinen Dienst, zwei bis vier Monate für eine mittelgroße Codebasis mit WCF, EF6 und einem WebForms- oder MVC 5-Frontend. WebForms-Anwendungen bleiben, wo sie sind, oder werden neu geschrieben; es gibt keinen In-Place-Port. Dieser Beitrag legt net48 als Quelle und net11.0 als Ziel fest und geht davon aus, dass das Projekt unter Windows läuft.
Warum jetzt migrieren
- .NET Framework 4.8 befindet sich seit dem 4.8.1 Release im August 2022 im reinen Wartungsmodus. Keine neuen APIs, keine neuen C#-Sprachfunktionen. Die .NET 11-Laufzeit liefert dynamisches PGO standardmäßig aktiviert, den modernisierten Tiered JIT und Native AOT für ASP.NET Core Minimal APIs.
- Jedes neue Microsoft-Framework (Aspire, Microsoft Agent Framework, Semantic Kernel 1.x, Azure Functions isolated worker) zielt auf
net8.0oder neuer ab und wird nicht zurückportiert. Auf 4.8 zu bleiben bedeutet, dass keines davon aus Ihrem eigenen Prozess heraus erreichbar ist. - Cloud-Kosten. Der Laufzeit-Speicherverbrauch von .NET 11 für eine im Leerlauf befindliche ASP.NET Core Minimal API liegt bei vergleichbarer Last bei etwa 35 bis 50 Prozent eines ASP.NET 4.8 Worker-Prozesses, was sich direkt in kleinere App Service-Pläne oder höhere Pod-Dichte in Kubernetes übersetzt.
- Recruiting und Tooling. Roslyn-Analyzer, Source Generators und die moderne
dotnetCLI setzen ein SDK-Style-Projekt voraus. Die C#-Sprachversion ist aufnet48auf C# 7.3 begrenzt, sofern Sie nicht gegen den Compiler ankämpfen, womit ein Jahrzehnt an Sprachfunktionen vom Tisch ist.
Wenn die Codebasis eine Windows-Desktop-Anwendung (WinForms oder WPF) ist, die nur unter Windows läuft und nirgendwo anders bereitgestellt werden soll, ist die Frage berechtigt. Die Antwort lautet trotzdem meistens ja, denn die unterstützte Lebensdauer von net48 endet mit dem erweiterten Support-Fenster von Windows 10, und die Tooling-Unterstützung ist bereits dünn.
Was bricht
| Bereich | Änderung | Schweregrad |
|---|---|---|
| Projektformat | packages.config und das alte csproj-XML werden vom .NET 11 SDK nicht unterstützt | hoch |
System.Web | Vollständig entfernt. HttpContext.Current, Module, Handler, WebForms haben kein .NET 11-Äquivalent | hoch |
| WCF-Server | System.ServiceModel auf der Serverseite wird nicht unterstützt. Verwenden Sie CoreWCF oder schreiben Sie auf gRPC oder HTTP um | hoch |
| WCF-Client | Unterstützt über System.ServiceModel.* 6.x NuGet-Pakete, mit eingeschränkten Bindings | mittel |
| Entity Framework 6 | Läuft auf .NET 11 mit EF6 6.5.0 oder neuer, neue Entwicklung sollte aber EF Core 11 verwenden | mittel |
AppDomain | Nur die Standard-AppDomain existiert. Kein CreateDomain, keine entladbaren Plugin-Container | hoch |
BinaryFormatter | In .NET 9 entfernt, kein Opt-in-Schalter | hoch |
| .NET Remoting | Weg. Kein Ersatz; schreiben Sie auf ein Netzwerkprotokoll um, das Sie tatsächlich wollen | hoch |
| Code Access Security | Weg. [SecurityCritical], PermissionSet, Sandboxing alles entfernt | hoch |
web.config | Konfiguration wandert nach appsettings.json. system.web-Abschnitte gelten nicht mehr | hoch |
app.config | Die meisten Einstellungen funktionieren weiterhin über Microsoft.Extensions.Configuration.Xml, aber Binding Redirects sind weg | mittel |
| WPF und WinForms | Unterstützt auf .NET 11, nur Windows. Die meisten Drittanbieter-Controls benötigen einen 6.x- oder neueren Build | mittel |
System.Drawing.Common | Plattformübergreifende Unterstützung in .NET 6 entfernt. Seither nur Windows | mittel |
Lesen Sie die .NET Framework to .NET porting overview und die .NET 11 breaking changes list einmal, bevor Sie eine .csproj anrühren. Die erste Liste ist mit Abstand die längere der beiden.
Pre-Flight-Checkliste
Führen Sie diese Schritte aus, bevor Sie eine einzige Projektdatei ändern.
- Installieren Sie das .NET 11 SDK auf jedem Entwicklerrechner und CI-Runner. Verifizieren Sie mit
dotnet --list-sdksund stellen Sie sicher, dass11.0.xerscheint. Lassen Sie das .NET Framework 4.8 Developer Pack installiert, damit sich die alte Solution weiterhin in Visual Studio öffnen lässt. - Installieren Sie die .NET Upgrade Assistant CLI und führen Sie sie zuerst im Analyse-Modus aus. Sie migriert keinen Code; sie erzeugt einen handlungsorientierten Bericht.
# .NET 11, upgrade-assistant 0.6.x dotnet tool install --global upgrade-assistant upgrade-assistant analyze MySolution.sln - Führen Sie den .NET Portability Analyzer oder
apiportgegen die kompilierten Assemblies aus. Alles, was als “not portable” markiert ist, ist Migrationsarbeit, die das upgrade-assistant-Tool nicht für Sie erledigen wird. - Erfassen Sie eine Baseline. Führen Sie die bestehende Test-Suite auf .NET Framework 4.8 aus und speichern Sie das Ergebnis. Ein sauberes Grün auf der alten Laufzeit bedeutet, dass das erste Rot auf .NET 11 eindeutig eine Migrationsregression ist.
- Inventarisieren Sie die NuGet-Pakete von Drittanbietern. Alles, was nur
net48- odernet472-Assemblies ausliefert, ist ein Blocker. Ersatz:log4net2.0.16,Newtonsoft.Json13.x,AutoMapper13.x,Dapper2.1.x sind alle Multi-Target und funktionieren auf .NET 11. Alles andere benötigt ein Upgrade-Ticket beim Hersteller. - Zweigen Sie die Migration ab. Planen Sie mindestens einen PR pro Projekt und einen separaten PR für die Testprojekte ein. Ein einzelner Mega-PR für eine mittelgroße Codebasis ist nicht reviewbar.
Migrationsschritte
-
Konvertieren Sie jede
.csprojin SDK-Style. Ersetzen Sie das alte XML durch den SDK-Style-Header. Das neue Format leitet Dateien ab, lässt die meisten Assembly-Referenzen weg und verwendetPackageReferencestattpackages.config. Dastry-convert-Tool (im .NET Upgrade Assistant gebündelt) übernimmt den mechanischen Teil.<!-- src/MyApi.csproj, .NET 11, after conversion --> <Project Sdk="Microsoft.NET.Sdk.Web"> <PropertyGroup> <TargetFramework>net11.0</TargetFramework> <LangVersion>14.0</LangVersion> <Nullable>enable</Nullable> <ImplicitUsings>enable</ImplicitUsings> </PropertyGroup> <ItemGroup> <PackageReference Include="Microsoft.AspNetCore.OpenApi" Version="11.0.0" /> <PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="11.0.0" /> </ItemGroup> </Project>Verifizierung:
dotnet buildbeendet sich mit Fehlern, die entfernte APIs benennen, anstatt mit Parser-Fehlern gegen die Projektdatei selbst. Löschen Siepackages.config, nachdem die Konvertierung erfolgreich war. -
Entfernen Sie die Nutzung von
BinaryFormatterüberall. Der Typ wurde in .NET 9 ohne Kompatibilitätsschalter entfernt. Ersetzen Sie ihn durchSystem.Text.Json, MessagePack oderprotobuf-net, je nachdem, ob Sie ein JSON- oder ein binäres Wire-Format benötigen. Wenn Sie gespeicherte Blobs haben, die mitBinaryFormatterserialisiert wurden, schreiben Sie ein einmaliges Konvertierungs-Utility, das noch auf .NET Framework 4.8 läuft, um sie in das neue Format zu übersetzen, bevor Sie die alte Umgebung außer Betrieb nehmen. Diese Konvertierung von .NET 11 aus durchzuführen ist nicht möglich.Verifizierung:
grep -r "BinaryFormatter" src/ist leer. Jeder Blob-Speicher, der zuvor binär formatierte Payloads enthielt, wurde neu serialisiert, und das neue Format wird über einen Unit-Test round-tripped. -
Schreiben Sie den Web-Stack von
System.Webauf ASP.NET Core 11 um. Dies ist die größte Einzelarbeit. MVC 5 Controller mappen sich nahezu eins zu eins auf ASP.NET Core Controller, aber Routing-Attribute, Model Binding, Action Filter und Dependency Injection unterscheiden sich alle.HttpContext.Currentist weg; Controller und Middleware erhaltenHttpContextexplizit.Application_StartinGlobal.asaxwird zu Startup-Code inProgram.cs. WebAPI 2 Controller sind ASP.NET Core Controllern sehr ähnlich, erben aber vonControllerBasestatt vonApiController.// Program.cs, .NET 11, C# 14 var builder = WebApplication.CreateBuilder(args); builder.Services.AddControllers(); builder.Services.AddOpenApi(); builder.Services.AddDbContext<AppDb>(o => o.UseSqlServer(builder.Configuration.GetConnectionString("Default"))); var app = builder.Build(); app.UseAuthentication(); app.UseAuthorization(); app.MapControllers(); app.MapOpenApi(); app.Run();WebForms (
.aspx) hat keinen Migrationspfad. Lassen Sie die WebForms-Anwendung entweder für den Rest ihres Lebens hinter einem Reverse Proxy auf .NET Framework 4.8 laufen, oder schreiben Sie die betroffenen Seiten als Blazor, MVC oder Razor Pages neu. Der Vergleich Blazor Server vs Blazor WebAssembly vs Blazor United ist der richtige Ausgangspunkt, falls Blazor zur Debatte steht.Verifizierung: Jeder Controller hat mindestens einen Integrationstest, der eine HTTP-Route gegen
WebApplicationFactory<Program>ausübt und sowohl Statuscode als auch Response-Body assertiert. -
Verschieben Sie
web.confignachappsettings.json. Connection Strings, eigene appSettings und Logging-Konfiguration wandern nach JSON.system.web-Abschnitte gelten nicht mehr.system.webServer-Abschnitte, die IIS konfigurieren, gelten weiterhin, wenn Sie hinter IIS über das In-Process-Modul hosten, aber die meisten Produktivbereitstellungen verwenden heute Kestrel direkt. Authentifizierungseinstellungen wandern von den<authentication>- und<authorization>-web.config-Abschnitten zubuilder.Services.AddAuthentication(...)und der Authorization-Policy-API.// appsettings.json, .NET 11 { "ConnectionStrings": { "Default": "Server=.;Database=App;Trusted_Connection=True;TrustServerCertificate=True;" }, "Logging": { "LogLevel": { "Default": "Information", "Microsoft.AspNetCore": "Warning" } } }Verifizierung: Die Anwendung liest jeden zuvor konfigurationsgesteuerten Wert über
IConfigurationoder das stark typisierteIOptions<T>-Muster; keine Aufrufe vonConfigurationManager.AppSettingsüberleben im Produktivcode. -
Bewältigen Sie WCF. Serverseitiges WCF wird auf .NET 11 nicht unterstützt. Zwei realistische Wege:
- CoreWCF (der von der Community gepflegte Port). Fügen Sie
CoreWCF.Primitives,CoreWCF.Httpund die Bindings hinzu, die Sie tatsächlich verwenden. Die meistenBasicHttpBinding- undNetTcpBinding-Dienste migrieren mit einer Contract-Referenz und einemUseServiceModel-Konfigurationsaufruf. Streaming, Transaktionen und Sicherheit auf Nachrichtenebene haben unterschiedliche Unterstützungsgrade; prüfen Sie die CoreWCF compatibility matrix, bevor Sie sich festlegen. - Umschreiben auf gRPC oder eine HTTP-API. Höhere Decke, mehr Arbeit. Die richtige Wahl, wenn die WCF-Schnittstelle ohnehin nur von Clients konsumiert wurde, die Sie selbst kontrollieren.
Clientseitiges WCF wird über die
System.ServiceModel.*6.x NuGet-Pakete unterstützt mitBasicHttpBinding,NetTcpBinding(eingeschränkt) undWSHttpBinding(nur Transport-Security). Wenn Ihr ClientWSFederationHttpBindingoder Sicherheit auf Nachrichtenebene verwendet, werden Sie den Consumer umschreiben.Verifizierung: Jeder WCF-Endpunkt hat einen Contract-Test, der den .NET 11-Client entweder gegen den neuen CoreWCF-Host oder den umgeschriebenen Ersatz laufen lässt und dieselben Payloads wie der alte .NET Framework-Client assertiert.
- CoreWCF (der von der Community gepflegte Port). Fügen Sie
-
Verschieben Sie Entity Framework 6 nach EF Core 11 (wenn es sich lohnt). EF6 läuft auf .NET 11 über das
EntityFramework6.5.0-Paket, ein striktes Lift-and-Shift ist also möglich. Aber EF6 erhält keine neuen Funktionen, dieDbContext-API in EF Core liegt näher an dem, was Sie für die Dependency Injection in ASP.NET Core wollen, und die kompilierten Abfragen sowie die Übersetzung von Primitive Collections in EF Core 11 sind spürbar schneller. Für die meisten Teams ist die richtige Entscheidung, die Migration zuerst auf EF6 auszuliefern und dann in einem Folge-PR auf EF Core umzustellen. Der Beitrag EF Core compiled queries vs raw SQL vs Dapper quantifiziert die Hot-Path-Gewinne.Verifizierung: Der gewählte ORM besteht dieselbe Integrationstest-Suite, die unter EF6 auf .NET Framework lief, einschließlich aller Tests, die das generierte SQL assertiert haben.
-
Ersetzen Sie
AppDomain.CreateDomain-Plugin-Loader.AppDomainist auf .NET 11 keine Isolationsgrenze mehr; nur die Standard-Domain existiert. Plugin-Systeme, die zuvor Assemblies in eine Kind-AppDomainfür Unload-Semantik oder Fehler-Isolation geladen haben, müssen aufAssemblyLoadContextmitisCollectible: trueumsteigen undUnload()aufrufen, wenn sie fertig sind. Out-of-Process-Plugins überdotnet-Worker-Prozesse sind das sicherere Muster, wenn das Plugin nicht vertrauenswürdig ist.Verifizierung: Ein Unit-Test lädt eine Plugin-Assembly, ruft hinein, entlädt den
AssemblyLoadContextund assertiert, dass eineWeakReferenceauf den Kontext nach einemGC.Collect()-Zyklusnullwird. -
Auditieren Sie die C#-Sprachversionssprünge. Von C# 7.3 auf C# 14 sind zwölf Sprach-Releases in einem Schritt. Das meiste davon ist additiv und sicher, aber nullbare Referenztypen (eingeführt in C# 8) werden Tausende von Warnungen auf Legacy-Code hervorrufen, wenn Sie
<Nullable>enable</Nullable>global einschalten. Der realistische Pfad ist zuerst<Nullable>annotations</Nullable>(nur Annotationen, keine Diagnostik), dann Datei für Datei Konvertierung mittels#nullable enable-Pragmas. Die Änderung an der Overload-Resolution in C# 14 rund um Span-Overloads ist im Fix-Beitrag C# 14 overload resolution breaking change with spans dokumentiert.Verifizierung:
dotnet build -warnaserrorist sauber für den vereinbarten Nullable-Scope vor dem Merge. -
Aktualisieren Sie die CI-Runner-Images. Heben Sie in GitHub Actions
actions/setup-dotnetaufdotnet-version: 11.0.xan, aktualisieren Sie jedes Dockerfile-Base-Image aufmcr.microsoft.com/dotnet/sdk:11.0undmcr.microsoft.com/dotnet/aspnet:11.0, und entfernen Sie das alte .NET Framework MSBuild-Image. Falls ein Projekt weiterhin unter .NET Framework gebaut werden muss (zum Beispiel das einmaligeBinaryFormatter-Konvertierungstool aus Schritt 2), behalten Sie einen einzelnenwindows-2022-Runner mit installiertem .NET Framework 4.8 Developer Pack und schalten Sie ihn über einen Path-Filter scharf.Verifizierung: Ein Pipeline-Lauf auf einem Feature-Branch ist Ende-zu-Ende grün, einschließlich
dotnet publish, Container-Image-Build und Smoke-Deploy in eine Staging-Umgebung.
Verifizierung (Smoke-Checkliste)
Nach den obigen Schritten sollte die Anwendung jede Zeile dieser Liste bestehen, bevor der Migrations-PR mergt:
dotnet --list-sdkszeigt11.0.x, unddotnet --versionaus dem Repo-Root druckt11.0.x.dotnet restore && dotnet build -c Releasebeendet sich mit 0 und keinen Warnungen für den vereinbarten Nullable-Scope.dotnet test -c Releaseist grün, und die Testanzahl entspricht der .NET Framework 4.8-Baseline (oder übertrifft sie).dotnet publish -c Releaseerzeugt ein Self-Contained-Artefakt, das auf einem sauberen Staging-Host ohne installiertes .NET Framework 4.8 Redistributable bootet.- Jede HTTP-Route hat mindestens einen Integrationstest gegen
WebApplicationFactory<Program>. - Logs zeigen in Produktivcode-Pfaden keine First-Chance-Referenzen auf
BinaryFormatter,IWebHostBuilder,HttpContext.CurrentoderConfigurationManager. - Ein Staging-Deploy bedient den Golden Path; p50/p95-Latenz liegt innerhalb von 20 Prozent der .NET Framework-Baseline auf vergleichbarer Hardware.
Wenn eines davon fehlschlägt, hören Sie auf. Eine partielle Migration auf .NET 11 ist schlimmer als ein sauberes .NET Framework 4.8-Deploy, weil sie auf beide Laufzeiten festlegt.
Rollback
Die Migration ist nur so lange reversibel, wie das Datenbankschema und alle Wire-Formate unverändert geblieben sind. Der Tausch der Laufzeit selbst ist reversibel: Setzen Sie die .csproj-Änderungen zurück und installieren Sie das .NET Framework 4.8 Redistributable auf dem Host neu. Die Entscheidungen, die einen Rollback teuer machen, sind meist orthogonal:
- Eine neue EF Core 11-Migration wurde gegen die Produktivdatenbank ausgeführt. Setzen Sie zuerst das Schema zurück.
- JSON-Payloads wurden unter
System.Text.Json-Defaults neu serialisiert, die sich vonNewtonsoft.Jsonunterscheiden. Nachgelagerte Konsumenten, die per Pattern-Matching auf Feldreihenfolge oder Null-Handling reagieren, werden Drift sehen. - Die Authentifizierung wurde von
FormsAuthentication-Cookies auf die Data-Protection-Cookies von ASP.NET Core umgestellt. Bestehende Sessions sind in beiden Richtungen ungültig.
Der pragmatische Plan ist, das .NET Framework-Deploy für eine Woche nach Cutover in einem separaten Slot warm zu halten und den Cutover über ein Feature Flag am Load Balancer statt zur Deploy-Zeit zu schalten. Danach Fix-Forward.
Stolpersteine, auf die wir gestoßen sind
HttpClientauf .NET 11 erzwingt TLS Server Name Indication strikt. Aufrufe an interne Dienste, die ein Zertifikat ohne passenden SAN präsentieren, scheitern mitAuthenticationException. Entweder reparieren Sie das Zertifikat oder setzen SieSslOptions.RemoteCertificateValidationCallbackbewusst. Die Defaults von .NET Framework waren lockerer, und das hat die SAN-Lücke verdeckt.DateTime.Parseauf .NET 11 ist strenger bei mehrdeutigen Formaten als .NET Framework 4.8. Code, derDateTimeohne explizitenIFormatProviderdurch einen String round-tripped hat, wird bei Eingaben, die er zuvor akzeptiert hat, plötzlichFormatExceptionwerfen. Übergeben Sie immerCultureInfo.InvariantCultureund ein bekanntes Format. Der Fix JSON value could not be converted to System.DateTime fix deckt die häufigste Variante ab, wenn das Datum über JSON ankommt.Microsoft.Data.SqlClientersetztSystem.Data.SqlClientin jedem modernen Pfad. EF Core 11 willMicrosoft.Data.SqlClient7.x oder neuer. Ein transitiver Pin auf das alteSystem.Data.SqlClientkompiliert zwar, scheitert aber zur Laufzeit bei der TLS 1.3-Aushandlung gegen neuere SQL Server-Boxen.- Konfigurationsbinding ist in JSON case-sensitive, in
app.configcase-insensitive. Eine Eigenschaft namensMaxRetriesinappsettings.jsonbindet nicht aus einemmaxretries-Key. Dem .NET FrameworkConfigurationManagerwar das egal. HostingEnvironment.MapPathist weg. Ersetzen Sie es durchIWebHostEnvironment.ContentRootPathundPath.Combine. Die~/-Virtual-Path-Syntax wird in ASP.NET Core von nichts verstanden.- WCF-DataContract-Surrogates round-trippen nicht identisch durch CoreWCF. Wenn Sie von
IDataContractSurrogateabhängen, schreiben Sie einen Contract-Test, der das exakte Wire-Format vor und nach der Migration assertiert, nicht nur Objektgleichheit.
Verwandt
- Migrate from .NET 8 to .NET 11: the full checklist
- Native AOT vs ReadyToRun vs JIT in .NET 11
- Minimal APIs vs controllers in ASP.NET Core 11
- System.Text.Json vs Newtonsoft.Json in 2026
- EF Core 11 vs Dapper for bulk inserts: real benchmark
Comments
Sign in with GitHub to comment. Reactions and replies thread back to the comments repo.