Key takeaways
- .NET 8 and .NET 9 both leave support on November 10, 2026, so new migrations should target .NET 10 LTS, which is supported until November 14, 2028.
- .NET Framework 4.8 and 4.8.1 are still supported as Windows components, but they get no new features, and libraries such as EF Core only run on modern .NET.
- Microsoft has deprecated the .NET Upgrade Assistant and now points teams to the GitHub Copilot upgrade agent, which assesses, plans and commits each change.
- For large ASP.NET apps that must stay live, Microsoft recommends incremental migration: an ASP.NET Core front app that proxies unmigrated routes to the old app with YARP.
- System.Web, Web Forms, WCF services, EDMX models and abandoned third-party packages drive most of the effort, so find them before you estimate.
- A test suite around critical business logic is the cheapest insurance you can buy before changing the runtime.
To migrate a .NET Framework application to .NET 10, first inventory what ties it to the old runtime (System.Web, Web Forms, WCF services, EDMX models and Framework-only NuGet packages). Then convert projects to SDK-style, port shared libraries from the bottom of the dependency tree up, and move the web layer either in place (small apps) or incrementally behind an ASP.NET Core proxy (large apps that must stay live). Build a test safety net before you change the runtime, and plan the hosting change, usually to Linux containers, as part of the same project.
Target .NET 10, not .NET 8. According to Microsoft’s support policy, .NET 8 LTS and .NET 9 STS both leave support on November 10, 2026. .NET 10 LTS is supported until November 14, 2028. The rest of this playbook covers the support facts, the assessment, the tools Microsoft provides today, the two migration strategies, and what drives cost, with examples from projects our .NET development team has worked on.
Support status: .NET Framework vs modern .NET
People often say .NET Framework is “end of life.” For 4.8 and 4.8.1 that is not accurate, and the nuance matters when you make the business case.
| Runtime | Status (October 2026) | What it means |
|---|---|---|
| .NET Framework 4.8 / 4.8.1 | Supported as a Windows component | Follows the lifecycle of the Windows version it runs on. Security fixes, no new features. |
| .NET Framework 4.6.2 | Ends January 12, 2027 | Upgrade to 4.8.x at minimum if you cannot migrate yet. |
| .NET Framework 4.5.2, 4.6, 4.6.1 | Retired April 26, 2022 | No security updates. |
| .NET 10 (LTS) | Active, until November 14, 2028 | The target for new migrations. |
| .NET 8 (LTS) and .NET 9 (STS) | Maintenance, ends November 10, 2026 | Plan the move to .NET 10 now. |
| .NET Core 3.1 and .NET 5 | Out of support since 2022 | Upgrade them the same way you would a Framework app. |
So the case for migrating is rarely “the runtime stops working tomorrow.” It is that .NET Framework is frozen. EF Core, current ASP.NET Core features, Linux containers and most new libraries only target modern .NET, and every year the pool of maintained Framework-compatible packages shrinks. The same applies to platforms built on Framework: Umbraco 8, for example, reached end of life on February 24, 2025, while Umbraco 17 LTS runs on .NET 10.
A note on STS releases
Odd-numbered releases now get 24 months of support, and even-numbered LTS releases get 36. If you are already on modern .NET but on an STS version, put the next upgrade on the roadmap. Our Auto.co.il case study is a good example of a modern build: the third generation of the site runs on Umbraco 15 and .NET 9. A version-to-version upgrade like that is a far smaller job than leaving .NET Framework, but with .NET 9 support ending on November 10, 2026 it needs to be on the plan now. .NET 11, at release candidate stage in October 2026, will be another STS release, so .NET 10 LTS stays the safer migration target.
Step 1: Assess before you estimate
Most failed estimates come from finding a blocker halfway through. A one- to two-week assessment pays for itself. Check these areas:
Dependencies
- NuGet packages. For each package, does a version exist that targets .NET Standard 2.0, .NET 8 or later? Is it still maintained? Paid UI component suites and old PDF or reporting libraries are frequent problems.
- packages.config. Microsoft recommends converting to
PackageReferenceand SDK-style project files first. Both work on .NET Framework, so this can ship before the runtime change. - Internal libraries. Draw the dependency graph. Microsoft’s guidance is to upgrade libraries in postorder depth-first order: leaves first, the main web app last.
- Windows-only APIs. Registry, GDI+ and similar APIs are available through the Windows Compatibility Pack (
Microsoft.Windows.Compatibility), but they keep you on Windows hosts.
The web layer: System.Web, Web Forms and MVC
ASP.NET MVC and Web API apps map reasonably well to ASP.NET Core, although the hosting model, middleware pipeline, configuration and dependency injection all change. The hard part is System.Web.HttpContext spread through business code. Count the call sites; that number is a good proxy for effort.
ASP.NET Web Forms has no direct equivalent on modern .NET. The current Microsoft tooling includes a Web Forms to Blazor scenario, but treat Web Forms pages as a rewrite of the UI layer and budget accordingly.
Unavailable technologies
Microsoft lists these as not available on modern .NET: application domains, .NET Remoting, Code Access Security, security transparency, System.EnterpriseServices (COM+) and Windows Workflow Foundation. WCF services also need a decision. WCF client libraries work on modern .NET, and for the server side you can move to CoreWCF (a port of WCF with its own Microsoft-published support policy) or replace the service with gRPC or REST.
Data access: EF6 vs EF Core
EF Core does not run on .NET Framework, but EF6 runs on modern .NET. Microsoft’s recommended order is:
- Move the app to modern .NET while keeping EF6.
- Port a small part of the data layer to EF Core and run both side by side.
- Retire EF6 when the rest is ported.
Watch for EDMX designer models. EF Core does not support EDMX, so those models need to be reverse engineered from the database. EF6 Code First migration history also does not carry over: apply the final EF6 migration, then create an initial EF Core migration.
Assessment checklist
- Inventory of projects, target frameworks and project formats
- Package list with modern equivalents or replacements
- Count of
System.Webusages outside the web project - Web Forms pages, WCF services, Remoting, AppDomains, WF
- EF6 model type (EDMX, Code First, Database First)
- Authentication model (Forms auth, Windows auth, OWIN, identity provider)
- Session state usage and where it is stored
- Background jobs, scheduled tasks and Windows services
- Existing test coverage for business-critical flows
- Hosting, deployment and monitoring setup
Step 2: Pick the tools Microsoft provides today
The tooling changed in 2025 and 2026, and a lot of older blog posts are out of date.
.NET Upgrade Assistant is deprecated. The Microsoft Learn page now says it is “officially deprecated” and points to the GitHub Copilot modernization agent, included with Visual Studio 2026 and Visual Studio 2022 17.14.16 or later.
GitHub Copilot upgrade agent. Microsoft documents it as a Copilot agent available in Visual Studio, Visual Studio Code, GitHub Copilot CLI and GitHub.com. Supported paths include .NET Framework (any version) to .NET 8 or later, and .NET Core 1.x to 3.x or .NET 5+ to .NET 8 or later. It covers ASP.NET Core, ASP.NET Web Forms, Blazor, WPF, Windows Forms, class libraries, console apps and test projects, among others. Scenarios include SDK-style conversion, Newtonsoft.Json to System.Text.Json, System.Data.SqlClient to Microsoft.Data.SqlClient, and Web Forms to Blazor.
How it works, per the docs: it creates a branch, writes assessment.md, plan.md and tasks.md under .github/upgrades/{scenarioId}/, recommends a bottom-up, top-down or all-at-once strategy, and creates a Git commit for each step so you can review or roll back.
Our view: treat the agent as a fast junior pair. It is good at the mechanical work (project files, package updates, API replacements, build errors). It does not know which behavior your users depend on, so the tests and the review still belong to your engineers.
Other tools that still help:
- try-convert converts projects to SDK-style, but Microsoft notes it is not recommended for projects with custom build tasks, targets or imports.
- Platform compatibility analyzer flags APIs that throw
PlatformNotSupportedExceptionat runtime.
Step 3: Choose in-place or incremental migration
Microsoft’s migration guide offers a simple decision rule.
| Situation | Approach |
|---|---|
| Small or medium app, few dependencies, can freeze features briefly | In-place migration |
| Large production app that must stay live | Incremental migration |
| Unknown or outdated dependencies, heavy System.Web usage | Incremental migration |
In-place migration
Convert projects to SDK-style, retarget libraries, then port the web project. This is the fastest path, but problems pile up because nothing ships until everything works. It suits internal tools and apps with good test coverage.
Incremental migration with YARP (strangler fig)
Microsoft describes incremental migration as an implementation of the Strangler Fig pattern. You put a new ASP.NET Core app in front of the old ASP.NET app. The new app serves the routes you have migrated and forwards everything else to the Framework app through YARP (Yet Another Reverse Proxy). Over time more routes move, until the old app serves nothing and can be switched off.
The pieces Microsoft documents:
- YARP fallback route in the ASP.NET Core app for unmatched requests.
- System.Web adapters (
Microsoft.AspNetCore.SystemWebAdapters) so shared libraries that useHttpContextcan compile for both runtimes. - Remote app session and remote authentication, so the new app can read the old app’s session and defer sign-in to it during the transition.
- An optional Aspire integration (currently a prerelease package) for orchestrating both apps locally.
The core of the proxy, based on Microsoft’s remote app setup docs:
// Program.cs in the new ASP.NET Core app (based on Microsoft's remote app setup sample)
using Microsoft.AspNetCore.SystemWebAdapters;
using Microsoft.Extensions.Options;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddSystemWebAdapters()
.AddRemoteAppClient(options =>
{
options.RemoteAppUrl = new(builder.Configuration["RemoteAppUri"]);
options.ApiKey = builder.Configuration["RemoteAppApiKey"];
});
builder.Services.AddReverseProxy();
var app = builder.Build();
// Migrated endpoints are mapped here and win over the fallback.
app.MapForwarder("/{**catch-all}",
app.Services.GetRequiredService<IOptions<RemoteAppClientOptions>>()
.Value.RemoteAppUrl.OriginalString)
.WithOrder(int.MaxValue) // lowest priority, runs last
.ShortCircuit(); // skip remaining middleware for proxied calls
app.Run();
Two constraints from the docs are easy to miss. Both apps must use identical virtual directory layouts. And the Framework app should be reachable only through the proxy, not directly from the internet.
Plan the migration order by risk and value. Start with low-risk, read-only pages to prove the pipeline, then move the high-traffic or high-change areas where modern .NET pays back fastest.
Step 4: Build the testing safety net first
Changing the runtime changes behavior in small ways: culture and globalization defaults, JSON serialization, configuration loading, authentication cookies. Tests are how you find out before users do.
- Characterization tests around critical business logic: pricing, permissions, calculations, imports. Write them against the Framework version, then run them unchanged against the new one.
- Multi-target shared libraries (for example
net48;net10.0) so the same tests run on both runtimes during the transition. - HTTP-level smoke tests for key routes, comparing status codes and key content between old and new.
- Background job checks. Scheduled tasks and queues tend to break quietly.
On the Jongeneel platform we followed this pattern. The internal transport operations platform ran on Umbraco 8 and .NET Framework, and we moved it to Umbraco 17 on .NET 10. Before changing code, the team mapped the solution’s structure, dependencies and business-critical modules to expose the risks. xUnit and Moq tests kept checking critical business logic throughout the migration, and Hangfire jobs, notifications and scheduled tasks were maintained and extended. The deprecated NuPicker plugin had no path forward, so we replaced it with Contentment and rebuilt its behavior with equivalent features.
Step 5: Plan the deployment change
Leaving .NET Framework usually also means leaving IIS on Windows Server. Modern ASP.NET Core apps run on Linux, which opens up containers and cheaper hosting, as long as you have removed Windows-only dependencies.
- Containers. The .NET SDK can build an image without a Dockerfile:
dotnet publish --os linux --arch x64 /t:PublishContainer(.NET 8 SDK or later). A Dockerfile still makes sense when you need extra OS packages or a custom build. - Configuration.
web.configtransforms becomeappsettings.jsonplus environment variables and a secrets store. - State. In-process session and local file storage break as soon as you run more than one container. Move session and cache to a distributed store and files to blob or object storage.
- Observability. Set up structured logging and error tracking before go-live, not after.
On Jongeneel, Docker supports local development and deployment, CI/CD pipelines produce consistent builds, and background processing is monitored with Serilog and Sentry.
Common blockers
| Blocker | Typical fix |
|---|---|
HttpContext.Current in business code |
System.Web adapters during transition, then refactor to inject what the code needs |
| Web Forms UI | Rebuild pages in Razor Pages, MVC or Blazor; Copilot has a Web Forms to Blazor scenario |
| WCF service hosting | CoreWCF, or replace with gRPC or REST |
| EDMX models | Reverse engineer with EF Core, or keep EF6 on .NET 10 first |
| Abandoned third-party packages | Replace with a maintained alternative or rewrite the feature |
| Forms authentication cookies | Remote authentication during transition, then ASP.NET Core auth |
| AppDomains, Remoting, WF | Separate processes, gRPC or IPC, CoreWF |
| Windows-only APIs | Windows Compatibility Pack, or keep Windows containers for that service |
What drives effort
There is no honest universal estimate, but the same factors decide the size of every project:
- System.Web reach. How far
HttpContext, session and Forms auth have spread beyond the web project. - UI technology. MVC ports; Web Forms gets rebuilt.
- Dependency health. Each package with no modern version becomes a mini-project.
- Data layer. EF6 kept as-is is cheap; EDMX to EF Core is not.
- Test coverage. Low coverage means writing tests before migrating, which is real but worthwhile work.
- Number of runtimes in the estate. Mixed estates multiply the work.
That last point is common. On the HS High Stake business portal, OSKI developers built modules for a large portal whose stack spans .NET Framework 4.8, ASP.NET Core 3.1 and .NET 5.0 in a microservice architecture. In estates like that, every shared library has to compile for several targets, and two of those three runtimes are already out of Microsoft support. Microservices actually help here: each service can move to .NET 10 on its own schedule, starting with the ones that share the fewest libraries.
A phased plan you can adapt
- Weeks 1 to 2: assessment. Dependency graph, blocker list, test gaps, target architecture, rough estimate.
- Preparation (still on Framework). Upgrade to .NET Framework 4.8.x, convert to SDK-style and
PackageReference, update packages, fix warnings, add characterization tests. - Libraries. Port shared libraries bottom-up to .NET Standard 2.0 or multi-target with .NET 10.
- Web layer. In-place port, or stand up the ASP.NET Core proxy and migrate routes in slices.
- Data. Keep EF6 on .NET 10, then move to EF Core slice by slice if it is worth it.
- Hosting. Containers, CI/CD, distributed cache and storage, monitoring.
- Decommission. Remove the proxy fallback and switch off the Framework app.
After go-live, keep a slot for routine runtime upgrades. With LTS releases every two years, the next move (.NET 10 to the following LTS) is far easier if it is part of ongoing maintenance and support rather than another rescue project. If your application is built on Umbraco, the CMS upgrade and the runtime upgrade are usually one project, which is how we ran Jongeneel; our Umbraco development page covers that path.
- Nov 10, 2026.NET 8 LTS and .NET 9 STS leave support
- Nov 14, 2028.NET 10 LTS support ends
- 36 monthssupport for even-numbered LTS releases
- 24 monthssupport for odd-numbered STS releases
From Microsoft's .NET support policy as of October 2026.
- Small or medium app
- Few dependencies
- Can freeze features briefly
- Large production app that must stay live
- Unknown or outdated dependencies
- Heavy System.Web usage
- Routes migrate in slices behind a proxy
- 01 Assessment Weeks 1-2
Dependency graph, blockers, test gaps, rough estimate.
- 02 Preparation On Framework
4.8.x, SDK-style projects, characterization tests.
- 03 Libraries
Port bottom-up to .NET Standard 2.0 or multi-target.
- 04 Web layer
In-place port, or proxy and migrate routes in slices.
- 05 Data
Keep EF6 on .NET 10, then EF Core slice by slice.
- 06 Hosting
Containers, CI/CD, distributed cache, monitoring.
- 07 Decommission
Remove the proxy fallback, switch off Framework.
Sources
- .NET and .NET Core official support policy (dotnet.microsoft.com)
- Microsoft .NET Framework lifecycle (Microsoft Learn)
- .NET Upgrade Assistant overview (Microsoft Learn)
- Port from .NET Framework to .NET (Microsoft Learn)
- Migrate from ASP.NET Framework to ASP.NET Core (Microsoft Learn)
- Port from EF6 to EF Core (Microsoft Learn)


