Breaking Changes in .NET 10: A Migration Guide from .NET 8
by Cheyenne Sokkappa, on Jul 27, 2025 12:00:00 AM
New year, new .NET release. And while keeping up with version changes might feel like a full-time job (especially when your backlog is already giving you side-eye), it’s worth the effort. Especially when we’re talking Long Term Support releases like .NET 10.
Releasing this November, .NET 10 isn’t just another stop on the release train. It’s the LTS successor to .NET 8 and your next strategic landing pad. And no, you’re not missing anything by skipping .NET 9. For those of us focused on stability, supportability, and scaling cleanly across the enterprise, .NET 10 is where the action is.
In this post, we’ll walk you through how to prep your migration strategy from .NET 8 to .NET 10, step by step. We’ll cover patterns, tools, timelines, and all the “gotchas” that can make or break your upgrade. Spoiler: a little planning now saves a lot of panic later. Now, let’s map out your battle plan.
Timeline for Migration Planning
Microsoft releases major .NET versions annually in November, with LTS versions (like .NET 8 and .NET 10) supported for three years and STS versions (like .NET 9) for 18 months. Here's a high-level timeline to plan your migration, updated with Preview 6 details:
- .NET 8 End of Support (EOS): November 10, 2026. After this, no more security updates or fixes.
- .NET 10 Previews: Started in early 2025 (e.g., Preview 1 in February), with Preview 6 released July 15, 2025, focusing on quality improvements, build performance, and enhancements in areas like .NET MAUI, Windows Forms, and containers. Ongoing previews through summer 2025.
- .NET 10 General Availability (GA): November 2025, with LTS support until November 2028.
- Recommended Migration Window: Begin testing in Q3 2025 using previews, including Preview 6 for the latest changes. Complete migration by mid-2026 to avoid .NET 8 EOS risks. For large projects, allocate 3-6 months for assessment, upgrades, and testing.
Factor in dependencies like third-party libraries, which may need updates for .NET 10 compatibility. Use this period to run parallel environments (.NET 8 and .NET 10) for validation.
Comprehensive List of Breaking Changes
.NET 10 introduces breaking changes across various areas, primarily to improve performance, security, and consistency. These are documented officially and grouped by technology area below. Migrating from .NET 8 means addressing changes from both .NET 9 and .NET 10. This list includes updates from Preview 6, marked accordingly.
Containers
Change Title |
Description |
Reason |
Migration Advice |
Introduced In |
Default .NET images use Ubuntu |
Default container images now use Ubuntu. |
Standardization and support. |
Update Dockerfiles; test for Ubuntu-specific behaviors. |
Earlier Preview |
Core .NET Libraries
Change Title |
Description |
Reason |
Migration Advice |
Introduced In |
ActivitySource.CreateActivity and ActivitySource.StartActivity behavior change |
Altered behavior for activity creation and starting. |
Improved diagnostics consistency. |
Review and update tracing code; test for propagation issues. |
Earlier Preview |
C# 14 overload resolution with span parameters |
Changes in overload resolution for spans. |
Enhanced type inference rules. |
Resolve ambiguities by explicit casting or using ReadOnlySpan<T> over Span<T>. |
Earlier Preview |
Consistent shift behavior in generic math |
Uniform shift operations in generic math. |
Standardization. |
Update generic math code to align with new behaviors. |
Earlier Preview |
Default trace context propagator updated to W3C standard |
Propagator now follows W3C specs. |
Compliance with industry standards. |
Ensure distributed tracing tools are W3C-compatible. |
Earlier Preview |
DriveInfo.DriveFormat returns Linux filesystem types |
Now reports accurate Linux FS types. |
Accuracy on Linux. |
Adjust platform-specific file handling code. |
Preview 6 |
LDAP DirectoryControl parsing is more stringent |
Stricter parsing rules. |
Security enhancements. |
Validate LDAP controls; fix invalid formats. |
Earlier Preview |
MacCatalyst version normalization |
Updated version handling for MacCatalyst. |
Platform alignment. |
Update version checks in cross-platform code. |
Earlier Preview |
.NET runtime no longer provides default SIGTERM signal handler |
Removed default handler. |
Customization flexibility. |
Implement custom SIGTERM handling if needed. |
Earlier Preview |
System.Linq.AsyncEnumerable included in core libraries |
Now part of core; requires source changes. |
Library consolidation. |
Update imports and resolve namespace conflicts. |
Earlier Preview |
YMM embedded rounding removed from AVX10.2 |
Removed support for YMM rounding. |
Hardware optimization. |
Rewrite vectorized code avoiding this feature. |
Earlier Preview |
Extensions
Change Title |
Description |
Reason |
Migration Advice |
Introduced In |
ProviderAliasAttribute moved to Microsoft.Extensions.Logging.Abstractions assembly |
Attribute moved to a different assembly. |
Assembly reorganization. |
Update references to the new assembly. |
Earlier Preview |
Globalization
Change Title |
Description |
Reason |
Migration Advice |
Introduced In |
Environment variable renamed to DOTNET_ICU_VERSION_OVERRIDE |
Renamed ICU override variable. |
Clarity. |
Update env vars in configs/scripts. |
Earlier Preview |
Cryptography
Change Title |
Description |
Reason |
Migration Advice |
Introduced In |
OpenSSL cryptographic primitives aren't supported on macOS |
Removed OpenSSL support on macOS. |
Platform-native crypto preference. |
Switch to Apple Crypto APIs. |
Preview 6 |
X500DistinguishedName validation is stricter |
Tighter validation rules. |
Security. |
Fix invalid distinguished names in certs. |
Earlier Preview |
X509Certificate and PublicKey key parameters can be null |
Parameters now nullable. |
Flexibility. |
Add null checks in certificate handling. |
Earlier Preview |
Interop
Change Title |
Description |
Reason |
Migration Advice |
Introduced In |
Single-file apps no longer look for native libraries in executable directory |
Changed search paths for native libraries in single-file apps. |
Security and consistency. |
Bundle natives or adjust load paths. |
Preview 6 |
Specifying DllImportSearchPath.AssemblyDirectory only searches the assembly directory |
Limits search to assembly directory when specified. |
Predictability. |
Place libraries in assembly directory or adjust paths. |
Preview 5 |
Networking
Change Title |
Description |
Reason |
Migration Advice |
Introduced In |
HttpClient/SSLStream default certificate revocation check mode changed to Online |
Default now Online for revocation checks. |
Improved security. |
Set to Offline if needed. |
Preview 6 |
Streaming HTTP responses enabled by default in browser HTTP clients |
Streaming now default in browser clients. |
Performance. |
Disable streaming if required. |
Preview 3 |
SDK and MSBuild
Change Title |
Description |
Reason |
Migration Advice |
Introduced In |
.NET CLI --interactive defaults to true in user scenarios |
Defaults to interactive for user ops. |
Security (user consent). |
Set --interactive false for automation. |
Preview 3 |
Default workload configuration from 'loose manifests' to 'workload sets' mode |
Switched default mode. |
Improved management. |
Update to 'workload sets'. |
Preview 2 |
dotnet package list performs restore |
Now runs restore before listing. |
Accurate listing. |
Ensure restore prereqs met. |
Preview 4 |
dotnet restore audits transitive packages |
Audits transitive deps by default. |
Security. |
Address audit findings. |
Preview 3 |
MSBUILDCUSTOMBUILDEVENTWARNING escape hatch removed |
Removed env var for suppressing warnings. |
Enforce checks. |
Handle warnings directly. |
Preview 1 |
MSBuild custom culture resource handling |
Changed localization handling. |
Consistency/performance. |
Update scripts for new behavior. |
Preview 1 |
NU1510 is raised for direct references pruned by NuGet |
Warning for pruned references. |
Inform devs. |
Fix references or suppress. |
Preview 1 |
PackageReference without a version raises an error |
Requires version in PackageReference. |
Better dependency management. |
Specify versions. |
Earlier Preview |
ASP.NET Core
Change Title |
Description |
Reason |
Migration Advice |
Introduced In |
Response Streaming Enabled by Default for HttpClient Requests |
Streaming now default in Blazor. |
Performance. |
Opt-out via project file or env var for sync ops. |
Earlier Preview |
NavigationManager.NavigateTo During Static SSR No Longer Throws NavigationException |
No exception on navigation. |
Consistency with interactive rendering. |
Remove exception-handling code; use AppContext switch to revert. |
Earlier Preview |
BlazorCacheBootResources MSBuild Property Removed |
Property removed as files are now fingerprinted. |
Optimization. |
Remove the property from project files. |
Earlier Preview |
OpenAPI 3.1 Support and Breaking Changes |
Updated to OpenAPI.NET v2.0; interfaces for entities, removed Nullable prop. |
Standard compliance. |
Update transformers to use JsonNode; check nullability via Type. |
Earlier Preview |
New JSON Patch Implementation with System.Text.Json |
Replaced Newtonsoft with System.Text.Json. |
Performance and security. |
Install new package; avoid dynamic types like ExpandoObject. |
Earlier Preview |
Entity Framework Core (EF Core 10)
Change Title |
Description |
Reason |
Migration Advice |
Introduced In |
ExecuteUpdateAsync now accepts a regular, non-expression lambda |
Switched to Func<> over Expression<>. |
Simpler syntax. |
Rewrite dynamic expressions as funcs. |
Earlier Preview |
Using GetDateTimeOffset without an offset now assumes UTC (Sqlite) |
Assumes UTC for offset-less timestamps. |
Consistency. |
Adjust time zone logic; use AppContext switch to revert. |
Earlier Preview |
Writing DateTimeOffset into REAL column now writes in UTC (Sqlite) |
Converts to UTC before writing. |
Standardization. |
Handle UTC in queries; revert via switch. |
Earlier Preview |
Using GetDateTime with an offset now returns value in UTC (Sqlite) |
Returns UTC-kind DateTime. |
Accuracy. |
Update to expect UTC; revert via switch. |
Earlier Preview |
C# Compiler
Change Title |
Description |
Reason |
Migration Advice |
Introduced In |
scoped in a lambda parameter list is now always a modifier |
Treated as modifier, not type. |
Language evolution. |
Prefix type with @scoped. |
Earlier Preview |
Span<T> and ReadOnlySpan<T> overloads applicable in more scenarios |
New conversions cause ambiguities. |
Improved inference. |
Explicit casts or prioritize ReadOnlySpan. |
Earlier Preview |
Diagnostics now reported for pattern-based disposal in foreach |
Obsolete DisposeAsync reported. |
Better warnings. |
Update or suppress obsolete methods. |
Earlier Preview |
Set state of enumerator object to "after" during disposal |
MoveNext() returns false post-disposal. |
Correctness. |
Avoid resuming disposed enumerators. |
Earlier Preview |
UnscopedRefAttribute cannot be used with old ref safety rules |
Fails in older targets. |
Compatibility. |
Upgrade language version or avoid attribute. |
Earlier Preview |
Microsoft.CodeAnalysis.EmbeddedAttribute validated on declaration |
Strict shape validation. |
Enforcement. |
Ensure attribute matches required form. |
Earlier Preview |
Expression field in property accessor refers to synthesized backing field |
field keyword binds to backing field. |
Clarity. |
Rename conflicting members or use @field. |
Earlier Preview |
Variable named field disallowed in property accessor |
Can't declare local/param as field. |
Avoid conflicts. |
Rename variable. |
Earlier Preview |
record and record struct types cannot define pointer type members |
Pointer fields disallowed. |
Safety. |
Refactor to avoid pointers in records. |
Earlier Preview |
Other areas like Windows Forms (e.g., obsolete APIs) may apply; check official docs for full details.
Code Migration Patterns
Migrating code involves common patterns to address breaking changes:
- Project File Updates: Change <TargetFramework>net8.0</TargetFramework> to <TargetFramework>net10.0</TargetFramework>. For multi-targeting, use <TargetFrameworks>net8.0;net10.0</TargetFrameworks>.
- Deprecated API Replacement:
- Before (.NET 8): var activity = ActivitySource.StartActivity("Old");
- After (.NET 10): Review and adapt to new behaviors, e.g., ensure W3C compliance: var activity = ActivitySource.StartActivity("New", kind: ActivityKind.Internal);
- Nullability and Validation Fixes:
- Before: X509Certificate cert = new(key); (assuming non-null)
- After: X509Certificate cert = new(key ?? throw new ArgumentNullException());
- Async/Streaming Adjustments:
- For Blazor streaming: Add opt-out if sync needed: request.SetBrowserResponseStreamingEnabled(false);
- Time Zone Handling in EF Core:
- Before: var dt = reader.GetDateTimeOffset(0); (local assumption)
- After: Adjust for UTC: var dtUtc = reader.GetDateTimeOffset(0).ToUniversalTime();
- New in Preview 6 Examples:
- For DriveInfo on Linux: Update checks like if (drive.DriveFormat == "ext4") to handle new accurate formats.
- For OpenSSL on macOS: Switch to SecureTransport or equivalent: Use SSLProtocols without OpenSSL dependencies.
- For single-file native libs: Use NativeLibrary.Load with explicit paths or bundle properly.
- For revocation checks: Set SslClientAuthenticationOptions.RemoteCertificateValidationCallback to customize if Online mode causes issues.
Run dotnet build with warnings as errors to catch issues early.
Tool Support for Automated Migration
The .NET Upgrade Assistant is the primary tool for automating migrations. It's available as a Visual Studio extension or CLI (dotnet tool install -g upgrade-assistant).
- How to Use: Install via VS Marketplace or CLI. Right-click project in VS > "Upgrade" or run upgrade-assistant upgrade MyProject.csproj. Choose in-place, side-by-side, or incremental mode.
- Features: Analyzes code, updates project files, fixes incompatibilities, and generates reports. Supports ASP.NET, console apps, libraries, and more.
- For .NET 8 to .NET 10: Update the tool to the newest version for Preview 6 support. Combine with Roslyn analyzers for custom fixes.
Other tools include try-convert for partial rewrites.
Common Pitfalls and Solutions
- Pitfall: Ignoring Warnings/Obsoletes - Builds succeed but runtime fails (e.g., obsolete APIs).
- Solution: Treat warnings as errors (<TreatWarningsAsErrors>true</TreatWarningsAsErrors>). Suppress only after review.
- Pitfall: Dependency Conflicts - NuGet packages not updated for .NET 10.
- Solution: Run dotnet restore with audits; update packages via dotnet list package --outdated.
- Pitfall: Platform-Specific Issues - Changes like Ubuntu images, macOS crypto, or Linux DriveFormat break cross-platform code.
- Solution: Test on all targets (Windows, Linux, macOS) using CI/CD pipelines.
- Pitfall: Incomplete Testing - Overlooking behavioral changes (e.g., UTC assumptions in Sqlite, online revocation checks).
- Solution: Use unit/integration tests; run in .NET 10 Preview 6 environments early.
- Pitfall: Large Monoliths - Full migration overwhelms.
- Solution: Adopt incremental migration with side-by-side projects or microservices refactoring.
Migrating to .NET 10 isn’t just about staying current. It’s about setting your applications up for long-term success. With LTS support stretching to 2028, this release gives you the runway to modernize confidently without playing version hopscotch every 18 months.
The changes in .NET 10 are meaningful, but with the right planning, tooling, and testing strategy, the upgrade doesn’t have to be painful. Start by reviewing your dependencies, testing in Preview 6, and creating a clear timeline that gives your team space to adapt without firefighting.
When you’re ready to move forward, we’re right here with tools, services, and plenty of coffee.