01 / START HERE
Target risk before syntax.
Choose a representative, recoverable application for the first migration. Include real authentication, data access, and deployment paths. Use what it teaches you to plan the remaining estate.
Inventory the estate and its blockers
Record each app’s owner, business criticality, deployment model, target frameworks, SDK pin, packages, native libraries, database provider, and operating system. Flag abandoned dependencies and vendor controls without .NET 10 support. Include workers, scheduled jobs, desktop clients, and shared libraries.
Exit condition: Every app has an owner and a supported dependency and hosting path.
Capture the .NET 8 baseline
Run existing tests and record representative API responses, login flows, SQL results, latency, memory, and job completion. Add focused integration tests where coverage is missing. Preserve the current release artifact and rehearse rollback before changing the runtime.
Exit condition: The team can detect a regression and restore the previous release.
Align the build and deployment chain
Install a supported .NET 10 SDK on developer machines and CI agents. Update global.json, compatible IDE tooling, project targets, package management, and publish profiles together. For IIS, verify the Hosting Bundle; for containers, update build and runtime images; for self-contained apps, republish for every supported platform.
Exit condition: A clean agent can build and run the exact artifact intended for production.
Microsoft migration checklistProve the highest-impact paths first
Prioritize sign-in and authorization, writes and transactions, external API contracts, background processing, and deployment startup. Review changes introduced in both .NET 9 and .NET 10. A direct target change to .NET 10 does not remove the intermediate compatibility work; a production deployment of .NET 9 is not a prerequisite.
Exit condition: Critical workflows pass against realistic infrastructure and representative data.
02 / REVIEW BEFORE RELEASE
Where a successful build can still hide a break.
Use these as targeted investigation prompts. Not every item affects every application, and this is not an exhaustive breaking-change list.
Web apps & APIs Authentication · proxies · error handling
- Cookie authentication: Known API endpoints in .NET 10 return 401/403 for authentication failures instead of login/access-denied redirects. Check SPA clients, API consumers, and unauthorized integration tests.
- Forwarded headers: Trusted-proxy configuration affects scheme, redirect URLs, and authentication behind load balancers. Unknown-proxy headers were already restricted in serviced .NET 8/9 releases; verify your existing patch level and explicitly configure trusted proxies.
- Handled exceptions: .NET 10 suppresses diagnostics by default for exceptions handled by
IExceptionHandler.TryHandleAsync. Check that expected logs, metrics, and alerts still exist.
Data access & EF Core Providers · migrations · generated SQL
Confirm database-provider support before selecting an EF Core major version. Upgrade EF packages, provider, design-time tooling, and dotnet-ef coherently. Review both EF 9 and EF 10 changes when starting from EF 8.
- EF 9 can throw when applying migrations with pending model changes. Inspect the model snapshot and remove nondeterministic model-building values before release.
- EF 10 changes parameterized-collection translation. Compare generated SQL, query plans, and latency for large
Containsfilters. - EF 10 may add an Application Name to connection strings. Mixed EF/ADO.NET or Dapper transactions need checks for connection-pool changes and transaction escalation.
- On applicable Azure SQL/SQL Server compatibility settings, JSON mapping defaults can change. Review generated migrations before applying them.
Use production-like database versions. A runtime retarget alone does not justify an unreviewed schema migration.
Workers, configuration & serialization Startup order · nulls · persisted payloads
BackgroundService.ExecuteAsyncruns entirely on a background task in .NET 10. Code before its firstawaitno longer blocks other services from starting. Move required initialization into an appropriate lifecycle hook and test readiness and shutdown.- Configuration now preserves null values. Test missing, empty, and explicit-null settings, especially layered environment configuration.
BinaryFormatterthrows in .NET 9 and later. Locate any remaining usage and design an explicit data-format migration, including existing stored data.- Retest JSON/XML payload contracts and custom converters. Include round trips with older clients, persisted messages, enums, dates, and null values.
Containers, desktop & Blazor Platform-specific checks
Containers: Default .NET 10 Linux image tags use Ubuntu rather than Debian. Review OS packages, native dependencies, ICU, certificates, and image scanning; choose and test an explicit supported image variant. Container guidance
WPF/WinForms: Keep Windows-specific TFMs where needed. Check vendor controls, COM/native interop, installers, accessibility, printing, and UI regressions against the platform-specific compatibility lists. Desktop changes
Blazor: Review both migration guides for published asset caching, boot configuration, authentication redirects, and navigation behavior. Apply template-related changes only where the application adopts those settings. 8 → 9 guide · 9 → 10 guide
MAUI/mobile: Track workloads, platform SDKs, device testing, and platform support separately; this server-focused guide is not a complete mobile migration plan.
03 / MAKE THE CHANGE
Keep the first upgrade small and reviewable.
Make a migration branch. Pin the approved installed .NET 10 SDK in global.json, retarget the application, and update only the packages that need changing for compatibility or support. Preserve platform suffixes such as net10.0-windows where required; shared libraries may need multiple targets while consumers move.
<PropertyGroup>
<TargetFramework>net10.0</TargetFramework>
</PropertyGroup>Run from the solution directory. These are starting checks; use your solution/project arguments and the real publish profile or runtime identifier used by deployment.
dotnet --info
dotnet --list-sdks
dotnet restore
dotnet list package --outdated
dotnet list package --vulnerable --include-transitive
dotnet build --configuration Release --no-restore
dotnet test --configuration Release --no-build
# Publish the deployable project with its normal deployment options:
dotnet publish ./src/YourApp/YourApp.csproj --configuration ReleaseReview restore and analyzer output rather than suppressing warnings wholesale. Transitive NuGet auditing changes in .NET 10 can surface vulnerabilities that earlier pipelines did not flag. Separate required fixes from style-only refactors. SDK and NuGet changes
04 / PROVE IT IN STAGING
Release when the evidence is there.
Behavior
Critical user journeys, authorization boundaries, API contracts, database writes, and scheduled jobs pass. Test the published artifact, including restart and graceful shutdown.
Operations
Compare error rates, p95 latency, memory, CPU, and query performance with the .NET 8 baseline. Confirm health checks, logs, traces, and alerts reach their destinations.
Recovery
Retain the previous artifact and settings. Rehearse rollback. Database changes must support the old app during rollback, or have a separately tested recovery plan and backup.
Rollout
Release to a small cohort or canary first when supported. Set app-specific acceptance thresholds and an observation window in advance, then expand traffic deliberately.
Recommended release boundary: First ship a supported .NET 10 application with equivalent business behavior. Track larger architectural and UI changes as separately testable work.
05 / AFTER THE BASELINE IS STABLE
Turn the upgrade into lasting value.
These are optional investments, not prerequisites or automatic benefits of retargeting. Pick them using measured problems and expected return.
Make failures easier to diagnose
Standardize structured logs, request correlation, and OpenTelemetry traces/metrics. Follow one request through the API, database, and downstream calls. Success means faster diagnosis with useful signals and controlled telemetry volume.
OpenTelemetry in .NETMake future upgrades routine
Centralize package versions where useful, automate dependency review, and test supported target frameworks in CI. Replace unsupported libraries and add contract tests at integration boundaries. Prefer smaller monthly servicing updates to another large migration.
Simplify validation and API documentation
Evaluate .NET 10 Minimal API validation and built-in OpenAPI improvements on a bounded endpoint. Check generated client compatibility and error-response contracts before replacing existing libraries. Existing controllers do not need a rewrite.
ASP.NET Core 10 capabilitiesReduce latency and repeated work
Profile expensive queries, blocking I/O, and repeated reads. Evaluate HybridCache for suitable cached data, with deliberate expiration, invalidation, and tenant isolation. Benchmark the result under representative traffic.
Caching optionsExplore Native AOT and smaller deployments
Use a small compatible service or CLI to test startup and memory gains. Review reflection, dynamic code generation, library support, and trimming warnings first. Proceed only when the measured gain outweighs compatibility and maintenance costs.
Native AOT constraintsModernize the experience in focused slices
Use a stable platform to improve accessibility, responsive layouts, and a consistent design system such as Auro. Pilot a user journey, verify keyboard and assistive-technology behavior, and preserve authentication and API contracts. Keep UI adoption independent of the runtime migration.
Explore the Auro flight controlsStart with one application.
Inventory its dependencies, capture its behavior, and prove one .NET 10 deployment. That gives the rest of the migration a tested path to follow.
Discuss your upgrade with William