Migrating from .NET Framework to .NET 8: A Practical Guide

.NET Framework and modern .NET have different support lifecycles. Microsoft documents .NET Framework 4.8.1 and recommends modern .NET for new development. An existing Framework application needs an assessment of its dependencies, supported operating system and business requirements before deciding whether to migrate.

Support update — 10 September 2026: This guide uses .NET 8 examples. Microsoft's current support policy lists .NET 8 support ending on 10 November 2026 and .NET 10 LTS support ending on 14 November 2028. For a new migration, assess .NET 10 as the target and check library and hosting compatibility. An interim move to .NET 8 needs a follow-on upgrade plan before its support ends.

Should You Migrate?

A stable application may be a candidate for continued maintenance. Check its exact framework version, Windows version, dependencies and support lifecycle; do not assume a further decade of support. Microsoft lists Framework versions and Windows dependencies. Compare ongoing maintenance with migration against your requirements.

The business case for migration usually comes down to three factors: developer productivity (modern .NET has better tooling, faster build times, and hot reload), hosting flexibility (containers, Linux, Azure App Service without Windows licensing), and future-proofing (new libraries, frameworks, and Azure services increasingly target modern .NET only). If any of these matter to your organisation, migration is worth the investment.

There's also a recruitment angle that's easy to underestimate. Senior .NET developers increasingly filter job searches by framework version. A job posting that mentions .NET Framework or Web Forms attracts a smaller, less enthusiastic candidate pool than one advertising .NET 8 and ASP.NET Core. If you're struggling to hire, the technology stack might be part of the problem.

Step 1: Assess Your Codebase

Inventory the solution, NuGet packages and platform dependencies against the target .NET version. Check compatibility for Windows-specific features such as registry access, and review WCF and System.Drawing usage individually. Microsoft now marks .NET Upgrade Assistant as deprecated and points to its GitHub Copilot modernization tooling. Assess tooling against your organisation's source-code handling rules; manual assessment, incremental changes and regression tests remain part of the migration.

Assess Web Forms pages, WCF services, OWIN/Katana middleware and System.Web dependencies individually. The migration path depends on the target platform and supported compatibility options; do not assume every component can be retargeted unchanged.

Create a spreadsheet listing every project in your solution with columns for target framework, NuGet package compatibility, Windows-only API usage, and estimated migration effort. This gives you a clear picture of the total scope and helps you prioritise which projects to migrate first. Class libraries with no Windows dependencies are typically the easiest starting point.

Don't forget to assess your build and deployment pipeline. If you're using MSBuild targets or custom build scripts that depend on Framework-specific tooling, these will need updating too. Similarly, if your deployment process copies specific DLLs from a bin folder, it won't work with the self-contained deployment model that modern .NET encourages.

Step 2: Prepare Your Solution

Before touching the target framework, clean up. Update all NuGet packages to their latest Framework-compatible versions. Replace deprecated APIs. Add unit tests for critical paths if you don't have them — you'll need them to verify nothing breaks during migration.

This preparation phase is where most teams cut corners and then pay for it later. Migrating and modernising simultaneously multiplies complexity. If you update packages, replace deprecated code, and change the target framework all at once, debugging failures becomes nearly impossible because you can't tell which change caused the problem.

I also recommend switching to the SDK-style project format before migrating frameworks. The old verbose .csproj format with hundreds of lines of item groups can be converted to the new SDK-style format while still targeting .NET Framework. This makes the actual framework change much cleaner because the project file is already in modern format.

If your solution uses packages.config for NuGet management, migrate to PackageReference first. Again, this can be done while still targeting Framework and eliminates one of the most common sources of confusion during migration.

Step 3: Migrate Incrementally

Start with class libraries — they're usually the easiest. Convert them to .NET Standard 2.0 first (compatible with both Framework and modern .NET), then move to .NET 8 once all consumers are ready. Use multi-targeting if you need to support both temporarily.

For web projects, the jump from MVC 5 to ASP.NET Core MVC is the biggest change. The request pipeline, dependency injection, configuration system, and middleware model are all different. Plan for this to take the most time and the most testing effort.

The order I recommend for a typical solution: shared utility libraries first (these usually have no Framework dependencies), then data access libraries (Entity Framework 6 to EF Core is a significant but well-documented migration), then business logic layers, then service/API projects, and finally the web frontend project last because it has the most Framework-specific code.

At each step, verify that the migrated project compiles, tests pass, and the application still functions correctly. Don't batch multiple project migrations into a single commit — if something breaks, you need to know exactly which change caused it.

Step 4: Handle Breaking Changes

Review request handling, authentication, application startup and frontend build tooling as separate migration tasks. Test the intended behaviour against the target ASP.NET Core version and record any compatibility changes.

Configuration is another area where the patterns change completely. Web.config with ConfigurationManager is replaced by appsettings.json with the Options pattern. Environment-specific configuration uses appsettings.Development.json rather than config transforms. This is actually a significant improvement once you get used to it, but it requires rewriting how your application reads configuration values.

Dependency injection deserves special attention. If your Framework application doesn't use DI (many older applications resolve dependencies manually or use Service Locator), you'll need to refactor as you migrate. ASP.NET Core's entire architecture assumes DI — controllers, middleware, services, and configuration are all resolved through the DI container. This is usually the healthiest change in the entire migration, but it touches a lot of code.

Step 5: Test and Deploy

Run your full test suite. If you added tests during the preparation phase, they'll prove their value here. Do side-by-side deployment if possible — run the old and new versions in parallel and compare outputs. Monitor performance closely after deployment: .NET 8 is generally faster, but behaviour differences can surface in edge cases.

Pay particular attention to DateTime handling (subtle differences exist between Framework and modern .NET in how certain date formats are parsed), string comparison behaviour (the default culture-sensitive comparison rules have changed in some cases), and JSON serialisation (if you're switching from Newtonsoft.Json to System.Text.Json, the default behaviour is different for property naming, null handling, and enum serialisation).

For the deployment itself, modern .NET gives you options that Framework didn't. You can deploy as a framework-dependent application (smaller deployment, requires the runtime on the server), a self-contained application (larger but includes everything needed to run), or a single-file executable. For most web applications, framework-dependent deployment to Azure App Service or a Linux container is the simplest approach.

The Payoff

Measure performance using representative workloads before and after a migration. Runtime and library changes may improve throughput or memory use, but a fixed speed increase is not guaranteed. Evaluate the deployment options and language features against the application’s needs.

Include maintenance effort, skills and recruitment needs in the business case. The value of a migration depends on the application and team; validate these assumptions instead of promising a recruitment or financial outcome.

Need Help?

I specialise in migrating .NET Framework applications to modern .NET. If you're considering a migration and want an honest assessment of effort and complexity, get in touch for a free 30-minute discovery call. I'll review your situation and give you a realistic picture of what's involved.

Need Help With This?

I offer consulting and hands-on development for .NET, Azure, and DevOps projects. Let's talk about how I can help.

Get in Touch →