Services
Custom software developmentUmbraco developmentWeb developmentCRM developmentERP developmentAI & LLM integration.NET development & modernizationE-commerce developmentIntegrations & automationCloud & DevOpsMaintenance, support & project rescueDedicated teams & white-label
Our Work
Case studiesProducts
About Blog Contact
.NET

How to Migrate .NET Framework to .NET 10: A Practical Playbook for 2026

How to move a .NET Framework 4.x application to .NET 10 without a risky big-bang rewrite: support status, assessment, Microsoft's current tooling, incremental migration with YARP, tests, containers and the blockers that drive effort.

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 PackageReference and 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:

  1. Move the app to modern .NET while keeping EF6.
  2. Port a small part of the data layer to EF Core and run both side by side.
  3. 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.Web usages 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 PlatformNotSupportedException at 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 use HttpContext can 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.config transforms become appsettings.json plus 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:

  1. System.Web reach. How far HttpContext, session and Forms auth have spread beyond the web project.
  2. UI technology. MVC ports; Web Forms gets rebuilt.
  3. Dependency health. Each package with no modern version becomes a mini-project.
  4. Data layer. EF6 kept as-is is cheap; EDMX to EF Core is not.
  5. Test coverage. Low coverage means writing tests before migrating, which is real but worthwhile work.
  6. 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

  1. Weeks 1 to 2: assessment. Dependency graph, blocker list, test gaps, target architecture, rough estimate.
  2. Preparation (still on Framework). Upgrade to .NET Framework 4.8.x, convert to SDK-style and PackageReference, update packages, fix warnings, add characterization tests.
  3. Libraries. Port shared libraries bottom-up to .NET Standard 2.0 or multi-target with .NET 10.
  4. Web layer. In-place port, or stand up the ASP.NET Core proxy and migrate routes in slices.
  5. Data. Keep EF6 on .NET 10, then move to EF Core slice by slice if it is worth it.
  6. Hosting. Containers, CI/CD, distributed cache and storage, monitoring.
  7. 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.

OSKI Engineering TeamSenior .NET, Umbraco and AI engineers

Written by the engineers who build and support OSKI client projects, reviewed by our CTO before publishing.

FAQ

Questions readers ask

Anything missing? Send us your question and an engineer will answer, usually within one business day.

Contact us
Is .NET Framework 4.8 end of life?+

No. .NET Framework 4.8 and 4.8.1 are components of Windows and follow the lifecycle of the Windows version they are installed on. They still receive security fixes, but no new features, and new libraries such as EF Core only target modern .NET.

Should I migrate to .NET 8 or .NET 10?+

Target .NET 10. Microsoft's support policy lists November 10, 2026 as the end of support for .NET 8, while .NET 10 LTS is supported until November 14, 2028. Starting a migration on .NET 8 today means planning another upgrade almost immediately.

Is the .NET Upgrade Assistant still supported?+

Microsoft lists the .NET Upgrade Assistant as officially deprecated. The recommended replacement is the GitHub Copilot upgrade (modernization) agent in Visual Studio, Visual Studio Code, GitHub Copilot CLI and GitHub.com, which supports .NET Framework (any version) to .NET 8 or later.

Can I keep Entity Framework 6 when moving to .NET 10?+

Yes. EF6 runs on modern .NET, and Microsoft suggests moving the runtime first while keeping EF6, then porting to EF Core later. EF Core does not support EDMX files, so models built with the designer need to be reverse engineered or rewritten.

How long does a .NET Framework to .NET migration take?+

It depends on how much code touches System.Web, whether you use Web Forms or WCF services, and how many dependencies have no modern version. Class libraries and console apps often port quickly; large ASP.NET apps usually need an incremental approach measured in months, not weeks.

What happens to WCF services when moving to .NET?+

The WCF server is not part of modern .NET. Options are CoreWCF, a community port that Microsoft publishes a support policy for, or replacing the service with gRPC or a REST API. The WCF client is available on modern .NET for calling existing services.

Start a project

Tell us what you need to ship. We come back with a plan and a price.

A spec, a Figma file or three paragraphs is enough to start.

  1. First reply · from an engineer, not a sales rep
  2. 30-min call · with Roman, our CTO, to clarify scope
  3. After the call · a written proposal with plan, team and price
Roman Oleksuk
Roman Oleksuk, CTOPrefer to talk? Pick a slot in Roman's calendar
We reply from a human mailbox. No newsletters. Your brief stays under NDA on request. We use your details only to reply; see our privacy policy.