Back to blog

// OSSeva Blog

Migration

.NET 8 to .NET 10 Upgrade Guide: Breaking Changes, SDK Steps and a Test Plan

Matt Reynolds11 min read

The short answer

Go from .NET 8 straight to .NET 10. You do not need to stop on .NET 9 first, and you should not: .NET 9 leaves support on 10 November 2026, the same day as .NET 8. .NET 10 is the current LTS release, and Microsoft supports it until 14 November 2028.

The mechanical part is small. Install the .NET 10 SDK, change the target framework to net10.0, and move the Microsoft packages to 10.0.x. The real work is that a jump from 8 to 10 crosses two releases, so the breaking changes for .NET 9 and for .NET 10 both apply. This guide covers the ones that break real applications, then gives a test plan and a timeline that ends before 10 November. The dates themselves are on the .NET 8 end-of-life page and the .NET 9 end-of-life page.

Before you change anything

  • List every application and what it targets. Search for TargetFramework and TargetFrameworks across repositories, plus any global.json that pins an SDK.
  • Note how each one ships. A framework-dependent app uses the runtime on the host. A self-contained app carries its own runtime and has to be rebuilt and redeployed. Container images carry the runtime in the base image tag.
  • Check the IDE. Microsoft supports targeting net10.0 in Visual Studio 2026 (version 18.0) and later. Visual Studio 2022 will not get .NET 10 support, so developers and build agents that use it need to move.
  • Pick a tool for the boring edits. Microsoft has deprecated the .NET Upgrade Assistant. Its replacement is the GitHub Copilot modernization agent in Visual Studio. A plain branch and a careful diff also works for most solutions.

Step 1: SDK, global.json and the target framework

Put the .NET 10 SDK on every build agent first, then change the project files. If a global.json pins the SDK, update it in the same commit or the build will keep using the old SDK.

<!-- .csproj or Directory.Build.props -->
<TargetFramework>net10.0</TargetFramework>

// global.json
{ "sdk": { "version": "10.0.100", "rollForward": "latestFeature" } }

Setting the target framework to net10.0 also moves the default C# language version to C# 14. One effect is listed as a breaking change: overload resolution now prefers some span-based overloads, so a call can bind to a different method than it did before. Rebuild and read the warnings rather than suppress them.

Libraries shared between applications on different schedules can multi-target with <TargetFrameworks>net8.0;net10.0</TargetFrameworks> during the move. Remember that from EF Core 10 the dotnet ef tools refuse to guess on a multi-targeted project, so add --framework net10.0 to every migration command.

Step 2: packages and restore

Move every Microsoft.AspNetCore.*, Microsoft.EntityFrameworkCore.* and Microsoft.Extensions.* package to the 10.0.x line in one change. Mixed major versions compile more often than they should and fail at runtime. Then work through third-party packages with dotnet list package --outdated. A package with no .NET 10 compatible release is the item most likely to move your date.

Expect restore to get stricter. In .NET 10, dotnet restore audits transitive packages for known vulnerabilities, not just direct ones. A solution built with warnings as errors can fail on the first restore with findings that were always there. A PackageReference without a version now raises an error, and NuGet can raise NU1510 for direct references it prunes.

Breaking changes that hit real applications

Microsoft publishes the full lists under .NET compatibility on learn.microsoft.com, one page per release. These are the entries we see matter most for server workloads moving from 8 to 10.

ChangeReleaseWhat to do
BinaryFormatter always throws.NET 9Find every use, including in old caching and session code, and replace it with a supported serializer.
Some X509Certificate2 constructors are obsolete.NET 9Load certificates with X509CertificateLoader.
HttpClientFactory uses SocketsHttpHandler as the primary handler, and its logging redacts header values.NET 9Retest custom handlers, proxies and anything that reads headers from logs.
Default container tags use Ubuntu; no Debian images for .NET 10.NET 10Test on Ubuntu 24.04 "Noble" images, or build and maintain your own Debian image.
OpenSSL 1.1.1 or later required on Unix.NET 10Check older Linux hosts and custom base images.
The runtime no longer provides default termination signal handlers.NET 10Apps not built on the generic host must handle SIGTERM themselves to shut down cleanly in containers.
BackgroundService runs all of ExecuteAsync as a Task.NET 10Code before the first await no longer runs during host startup. Move anything startup depends on into StartAsync.
Null values preserved in configuration.NET 10Check binding code that assumed an explicit null became an empty value.
System.Text.Json checks for property name conflicts.NET 10Fix types where two members map to the same JSON name.
Default trace context propagator updated to W3C.NET 10Confirm tracing still links across services that use another format.

Windows Forms and WPF have their own obsoletions in both releases. Desktop applications should read those sections in full.

ASP.NET Core 8 to 10

  • WebHostBuilder, IWebHost and WebHost are obsolete. Apps still on the old Startup pattern should move to WebApplication or the generic host.
  • Cookie login redirects are disabled for known API endpoints. An unauthenticated call to an API endpoint now gets 401 or 403 instead of a redirect to the login page. Clients that followed the redirect need a fix.
  • IActionContextAccessor and ActionContextAccessor are obsolete.
  • IPNetwork and ForwardedHeadersOptions.KnownNetworks are obsolete. Review proxy configuration for apps behind a load balancer.
  • Razor runtime compilation is obsolete, and the WithOpenApi extension method is deprecated.
  • Exception diagnostics are suppressed when an IExceptionHandler's TryHandleAsync returns true. If alerts depended on those logs, add your own.

EF Core 8 to 10

EF Core 9 still runs on .NET 8, so you can take the EF Core 9 changes first and keep the runtime move separate. Two of them are rated high impact by Microsoft:

  • Migrate throws if the model has pending changes. A model built non-deterministically, for example with DateTime.Now in HasData, now stops deployment.
  • Migrate throws inside an explicit transaction. Remove the wrapping transaction; EF manages it.

EF Core 10 adds its own:

  • Parameterized collections use multiple parameters by default. A Contains over a list now becomes IN (@ids1, @ids2, ...) instead of a JSON parameter. Measure queries with large lists.
  • SQL parameter names are simplified, from @__city_0 to @city. Snapshot tests that compare SQL text will fail, and Microsoft notes that most cached query plans will recompile after deployment.
  • The SQL Server json type is used on Azure SQL and at compatibility level 170. On those targets the upgrade generates a migration that alters existing JSON columns.
  • ExecuteUpdateAsync takes a regular lambda. Code that built expression trees for the setters no longer compiles, and its replacement is much shorter.
  • Microsoft.Data.Sqlite now treats timestamps without an offset as UTC. Rated high impact for SQLite users.

SDK and build behaviour

  • The terminal logger is the default from .NET 9, and in .NET 10 some dotnet commands write non-command output to stderr. CI scripts that parse output need checking.
  • --interactive defaults to true in user scenarios. Pipelines are unaffected, but scripts run by people may now prompt.
  • dotnet new sln creates the SLNX format.
  • Code coverage no longer turns on dynamic native instrumentation by default.

A test plan for the upgrade

  1. Build every project on the branch with warnings as errors and clear the new obsoletion warnings.
  2. Run unit tests, then integration tests against real dependencies: the database, the message broker, the identity provider.
  3. Generate an EF migration and confirm it is empty, or review exactly what it changes. Apply it to a copy of production data.
  4. Run API contract tests. Look for 401 responses where clients expected a redirect, and JSON output that changed shape.
  5. Smoke test the new container image on the Ubuntu base, including native dependencies such as OpenSSL and ICU.
  6. Compare performance against the .NET 8 build, especially queries with large IN lists and the first hour after deployment while plans recompile.
  7. Send SIGTERM to a running container and check that in-flight work completes.
  8. Scan the built image, then release to a canary with the .NET 8 build kept ready for rollback.

A timeline that ends before 10 November 2026

DatesWork
5 to 9 OctoberInventory, .NET 10 SDK on build agents, Visual Studio 2026 for developers, one upgrade branch per application.
12 to 23 OctoberTarget framework and package changes, build fixes, EF Core 9 then 10, the first container images.
26 October to 6 NovemberThe test plan in staging, performance comparison, canary releases.
By 10 NovemberProduction on .NET 10, or a documented exception for each application that is not.

Most estates have a few applications that will not fit this window: a vendor product certified only on .NET 8, a library with no .NET 10 release, or a regulated system that needs a new validation cycle. Decide which ones they are in the first week, not the last.

Applications still on .NET 8 after 10 November

After that date Microsoft ships no more fixes for .NET 8 or .NET 9. OSSeva provides .NET 8 extended support for the applications that miss the window: patched runtime builds, VEX attestations for scanner findings, and 24/7 operations, while the upgrade continues on its own schedule. The .NET end-of-life chart shows every version's dates, and the .NET support overview lists what is covered.

Frequently asked questions

Can I upgrade from .NET 8 directly to .NET 10?

Yes. There is no need to install or target .NET 9 on the way. Review the .NET 9 and .NET 10 breaking change lists together, because you cross both.

Will a .NET 8 app run on the .NET 10 runtime without a rebuild?

Not by default. A framework-dependent app only rolls forward to a newer major version if its roll-forward policy allows it. Retargeting and testing is the supported path.

Do I need Visual Studio 2026 to target .NET 10?

For supported tooling, yes. Microsoft supports net10.0 in Visual Studio 2026 (18.0) and later, and Visual Studio 2022 will not add it. The .NET CLI works without either.

How long is .NET 10 supported?

.NET 10 is an LTS release, supported until 14 November 2028.

Should we wait for .NET 11?

No. .NET 11 is a Standard Term release. For systems that should not need another major upgrade soon, .NET 10 is the target.

Tags

.NET.NET 8.NET 10UpgradeMigrationEF CoreASP.NET Core

Ready to get your open source under control?

Talk to an OSSeva engineer about CVE coverage, compliance, and migration support for your stack.