Claude Cursor GitHub Copilot Skill

migrate-dotnet9-to-dotnet10

Migrate a .NET 9 project or solution to .NET 10 and resolve all breaking changes. USE FOR: upgrading TargetFramework from net9.0 to net10.0, fixing build errors after updating the .NET 10 SDK, resolving source and behavioral changes in .NET 10 / C# 14 / ASP.NET Core 10 / EF Core

LLM Mart · 0 points · 17 views 0 listing impressions 0 install-command copies
Virus-scanned Reviewed automatically before listing.

Full trust report

Download dotnet-skills-plugins_dotnet-upgrade_skills_migrate-dotnet9-to-dotnet10-98f8485.zip · 26 KB
Part of dotnet/skills — 119 skills

Install

skills CLI npx skills add https://github.com/dotnet/skills/tree/main/plugins/dotnet-upgrade/skills/migrate-dotnet9-to-dotnet10
Claude Code claude plugin marketplace add https://llmmart.ai/marketplace.json && claude plugin install dotnet-skills@llmmart
Git git clone https://github.com/dotnet/skills.git

The skills CLI installs just this skill, for any of its supported agents. Claude Code installs the whole dotnet/skills collection as a plugin from our marketplace. Git is the plain clone.

Skill manifest

.NET 9 → .NET 10 Migration

Migrate a .NET 9 project or solution to .NET 10, systematically resolving all breaking changes. The outcome is a project targeting net10.0 that builds cleanly, passes tests, and accounts for every behavioral, source-incompatible, and binary-incompatible change introduced in the .NET 10 release.

When to Use

  • Upgrading TargetFramework from net9.0 to net10.0
  • Resolving build errors or new warnings after updating the .NET 10 SDK
  • Adapting to behavioral changes in .NET 10 runtime, ASP.NET Core 10, or EF Core 10
  • Updating CI/CD pipelines, Dockerfiles, or deployment scripts for .NET 10
  • Migrating from the community System.Linq.Async package to the built-in System.Linq.AsyncEnumerable

When Not to Use

  • The project already targets net10.0 and builds cleanly — migration is done
  • Upgrading from .NET 8 or earlier — use the migrate-dotnet8-to-dotnet9 skill first to reach net9.0, then return to this skill for the net9.0 → net10.0 migration
  • Migrating from .NET Framework — that is a separate, larger effort
  • Greenfield projects that start on .NET 10 (no migration needed)

Inputs

Input Required Description
Project or solution path Yes The .csproj, .sln, or .slnx entry point to migrate
Build command No How to build (e.g., dotnet build, a repo build script). Auto-detect if not provided
Test command No How to run tests (e.g., dotnet test). Auto-detect if not provided
Project type hints No Whether the project uses ASP.NET Core, EF Core, WinForms, WPF, containers, etc. Auto-detect from PackageReferences and SDK attributes if not provided

Workflow

Answer directly from the loaded reference documents. Do not search the filesystem or fetch web pages for breaking change information — the references contain the authoritative details. Focus on identifying which breaking changes apply and providing concrete fixes. Exception: If you suspect a security vulnerability (CVE) may apply to the project's dependencies, check for published security advisories — the reference documents may not cover post-publication CVEs.

Commit strategy: Commit at each logical boundary — after updating the TFM (Step 2), after resolving build errors (Step 3), after addressing behavioral changes (Step 4), and after updating infrastructure (Step 5). This keeps each commit focused and reviewable.

Step 1: Assess the project

  1. Identify how the project is built and tested. Look for build scripts, .sln/.slnx files, or individual .csproj files.
  2. Run dotnet --version to confirm the .NET 10 SDK is installed. If it is not, stop and inform the user.
  3. Determine which technology areas the project uses by examining:
    • SDK attribute: Microsoft.NET.Sdk.Web → ASP.NET Core; Microsoft.NET.Sdk.WindowsDesktop with <UseWPF> or <UseWindowsForms> → WPF/WinForms
    • PackageReferences: Microsoft.EntityFrameworkCore.* → EF Core; Microsoft.Data.Sqlite → Sqlite; Microsoft.Extensions.Hosting → Generic Host / BackgroundService
    • Dockerfile presence → Container changes relevant
    • P/Invoke or native interop usage → Interop changes relevant
    • System.Linq.Async package reference → AsyncEnumerable migration needed
    • System.Text.Json usage with polymorphism → Serialization changes relevant
  4. Record which reference documents are relevant (see the reference loading table in Step 3).
  5. Do a clean build (dotnet build --no-incremental or delete bin/obj) on the current net9.0 target to establish a clean baseline. Record any pre-existing warnings.

Step 2: Update the Target Framework

  1. In each .csproj (or Directory.Build.props if centralized), change:

    <TargetFramework>net9.0</TargetFramework>
    

    to:

    <TargetFramework>net10.0</TargetFramework>
    

    For multi-targeted projects, add net10.0 to <TargetFrameworks> or replace net9.0.

  2. Update all Microsoft.Extensions.*, Microsoft.AspNetCore.*, Microsoft.EntityFrameworkCore.*, and other Microsoft package references to their 10.0.x versions. If using Central Package Management (Directory.Packages.props), update versions there.

  3. Run dotnet restore. Watch for:

    • NU1510: Direct references pruned by NuGet — the package may be included in the shared framework now. Remove the explicit <PackageReference> if so.
    • PackageReference without a version now raises an error — every <PackageReference> must have a Version (or use CPM).
    • NuGet auditing of transitive packages (dotnet restore now audits transitive deps) — review any new vulnerability warnings.
  4. Run a clean build. Collect all errors and new warnings. These will be addressed in Step 3.

Step 3: Resolve build errors and source-incompatible changes

Work through compilation errors and new warnings systematically. Load the appropriate reference documents based on the project type:

If the project uses… Load reference
Any .NET 10 project references/csharp-compiler-dotnet9to10.md
Any .NET 10 project references/core-libraries-dotnet9to10.md
Any .NET 10 project references/sdk-msbuild-dotnet9to10.md
ASP.NET Core references/aspnet-core-dotnet9to10.md
Entity Framework Core references/efcore-dotnet9to10.md
Cryptography APIs references/cryptography-dotnet9to10.md
Microsoft.Extensions.Hosting, BackgroundService, configuration references/extensions-hosting-dotnet9to10.md
System.Text.Json, XmlSerializer, HttpClient, MailAddress, Uri references/serialization-networking-dotnet9to10.md
Windows Forms or WPF references/winforms-wpf-dotnet9to10.md
Docker containers, single-file apps, native interop references/containers-interop-dotnet9to10.md

Common source-incompatible changes to check for:

  1. System.Linq.Async conflicts — Remove the System.Linq.Async package reference or upgrade to v7.0.0. If consumed transitively, add <ExcludeAssets>compile</ExcludeAssets>. Rename SelectAwait calls to Select where needed.

  2. New obsoletion warnings (SYSLIB0058–SYSLIB0062):

    • SYSLIB0058: Replace SslStream.KeyExchangeAlgorithm/CipherAlgorithm/HashAlgorithm with NegotiatedCipherSuite — if the old properties were used to reject weak TLS ciphers, preserve equivalent validation logic using the new API
    • SYSLIB0059: Replace SystemEvents.EventsThreadShutdown with AppDomain.ProcessExit
    • SYSLIB0060: Replace Rfc2898DeriveBytes constructors with Rfc2898DeriveBytes.Pbkdf2
    • SYSLIB0061: Replace Queryable.MaxBy/MinBy overloads taking IComparer<TSource> with ones taking IComparer<TKey>
    • SYSLIB0062: Replace XsltSettings.EnableScript usage
  3. C# 14 field keyword in property accessors — The identifier field is now a contextual keyword inside property get/set/init accessors. Local variables named field cause CS9272 (error). Class members named field referenced without this. cause CS9258 (warning). Fix by renaming (e.g., fieldValue) or escaping with @field. See references/csharp-compiler-dotnet9to10.md.

  4. C# 14 extension contextual keyword — Types, aliases, or type parameters named extension are disallowed. Rename or escape with @extension.

  5. C# 14 overload resolution with span parameters — Expression trees containing .Contains() on arrays may now bind to MemoryExtensions.Contains instead of Enumerable.Contains. Enumerable.Reverse on arrays may resolve to the in-place Span extension. Fix by casting to IEnumerable<T>, using .AsEnumerable(), or explicit static invocations. See references/csharp-compiler-dotnet9to10.md for full details.

  6. ASP.NET Core obsoletions (if applicable):

    • WebHostBuilder, IWebHost, WebHost are obsolete — migrate to Host.CreateDefaultBuilder or WebApplication.CreateBuilder
    • IActionContextAccessor / ActionContextAccessor obsolete
    • WithOpenApi extension method deprecated
    • IncludeOpenAPIAnalyzers property deprecated
    • IPNetwork and ForwardedHeadersOptions.KnownNetworks obsolete
    • Razor runtime compilation is obsolete
    • Microsoft.Extensions.ApiDescription.Client package deprecated
    • Microsoft.OpenApi v2.x breaking changes — Microsoft.AspNetCore.OpenApi 10.0 pulls in Microsoft.OpenApi v2.x which restructures namespaces and models. OpenApiString/OpenApiAny types are removed (use JsonNode), OpenApiSecurityScheme.Reference replaced by OpenApiSecuritySchemeReference, collections on OpenAPI model objects may be null, and OpenApiSchema.Nullable is removed. See references/aspnet-core-dotnet9to10.md for migration patterns.
  7. SDK changes:

    • dotnet new sln now defaults to SLNX format — use --format sln if the old format is needed
    • Double quotes in file-level directives are disallowed
    • dnx.ps1 removed from .NET SDK
    • project.json no longer supported in dotnet restore
  8. EF Core source changes (if applicable) — See references/efcore-dotnet9to10.md for:

    • ExecuteUpdateAsync now accepts a regular lambda (expression tree construction code must be rewritten)
    • IDiscriminatorPropertySetConvention signature changed
    • IRelationalCommandDiagnosticsLogger methods add logCommandText parameter
  9. WinForms/WPF source changes (if applicable):

    • Applications referencing both WPF and WinForms must disambiguate MenuItem and ContextMenu types
    • Renamed parameter in HtmlElement.InsertAdjacentElement
    • Empty ColumnDefinitions and RowDefinitions are disallowed in WPF
  10. Cryptography source changes (if applicable):

  • MLDsa and SlhDsa members renamed from SecretKey to PrivateKey (e.g., ExportMLDsaSecretKey → ExportMLDsaPrivateKey, SecretKeySizeInBytes → PrivateKeySizeInBytes)
  • Rfc2898DeriveBytes constructors are obsolete (SYSLIB0060) — replace with static Rfc2898DeriveBytes.Pbkdf2(password, salt, iterations, hashAlgorithm, outputLength)
  • CoseSigner.Key can now be null — check for null before use
  • X509Certificate.GetKeyAlgorithmParameters() and PublicKey.EncodedParameters can return null
  • Environment variable renamed from CLR_OPENSSL_VERSION_OVERRIDE to DOTNET_OPENSSL_VERSION_OVERRIDE

Build again after each batch of fixes. Repeat until the build is clean.

Step 4: Address behavioral changes

Behavioral changes do not cause build errors but may change runtime behavior. Review each applicable item and determine whether the previous behavior was relied upon.

High-impact behavioral changes (check first):

  1. SIGTERM signal handling removed — The .NET runtime no longer registers default SIGTERM handlers. If you rely on AppDomain.ProcessExit or AssemblyLoadContext.Unloading being raised on SIGTERM:

    • ASP.NET Core and Generic Host apps are unaffected (they register their own handlers)
    • Console apps and containerized apps without Generic Host must register PosixSignalRegistration.Create(PosixSignal.SIGTERM, _ => Environment.Exit(0)) explicitly
  2. BackgroundService.ExecuteAsync runs entirely on a background thread — The synchronous portion before the first await no longer blocks startup. If startup ordering matters, move that code to StartAsync or the constructor, or implement IHostedLifecycleService.

  3. Configuration null values are now preserved — JSON null values are no longer converted to empty strings. Properties initialized with non-default values will be overwritten with null. Review configuration binding code.

  4. Microsoft.Data.Sqlite DateTimeOffset changes (all High impact):

    • GetDateTimeOffset without an offset now assumes UTC (previously assumed local)
    • Writing DateTimeOffset into REAL columns now converts to UTC first
    • GetDateTime with an offset now returns UTC with DateTimeKind.Utc
    • Mitigation: AppContext.SetSwitch("Microsoft.Data.Sqlite.Pre10TimeZoneHandling", true) as a temporary workaround
  5. EF Core parameterized collections — .Contains() on collections now uses multiple scalar parameters instead of JSON/OPENJSON. May affect query performance for large collections. Mitigation: UseParameterizedCollectionMode(ParameterTranslationMode.Parameter) to revert.

  6. EF Core JSON data type on Azure SQL — Azure SQL and compatibility level ≥170 now use the json data type instead of nvarchar(max). A migration will be generated to alter existing columns. Mitigation: set compatibility level to 160 or use HasColumnType("nvarchar(max)") explicitly.

  7. System.Text.Json property name conflict validation — Polymorphic types with properties conflicting with metadata names ($type, $id, $ref) now throw InvalidOperationException. Add [JsonIgnore] to conflicting properties.

Other behavioral changes to review:

  • BufferedStream.WriteByte no longer implicitly flushes — add explicit Flush() calls if needed
  • Default trace context propagator updated to W3C standard
  • DriveInfo.DriveFormat returns actual Linux filesystem type names
  • LDAP DirectoryControl parsing is more stringent
  • Default .NET container images switched from Debian to Ubuntu (Debian images no longer shipped)
  • Single-file apps no longer look for native libraries in executable directory by default
  • DllImportSearchPath.AssemblyDirectory only searches the assembly directory
  • MailAddress enforces validation for consecutive dots
  • Streaming HTTP responses enabled by default in browser HTTP clients
  • Uri length limits removed — add explicit length validation if Uri was used to reject oversized input from untrusted sources
  • Cookie login redirects disabled for known API endpoints (ASP.NET Core)
  • XmlSerializer no longer ignores [Obsolete] properties — audit obsolete properties for sensitive data and add [XmlIgnore] to prevent unintended data exposure
  • dotnet restore audits transitive packages
  • dotnet watch logs to stderr instead of stdout
  • dotnet CLI commands log non-command-relevant data to stderr
  • Various NuGet behavioral changes (see references/sdk-msbuild-dotnet9to10.md)
  • StatusStrip uses System RenderMode by default (WinForms)
  • TreeView checkbox image truncation fix (WinForms)
  • DynamicResource incorrect usage causes crash (WPF)

Step 5: Update infrastructure

  1. Dockerfiles: Update base images. Default tags now use Ubuntu instead of Debian. Debian images are no longer shipped for .NET 10.

    # Before
    FROM mcr.microsoft.com/dotnet/sdk:9.0 AS build
    FROM mcr.microsoft.com/dotnet/aspnet:9.0
    # After
    FROM mcr.microsoft.com/dotnet/sdk:10.0 AS build
    FROM mcr.microsoft.com/dotnet/aspnet:10.0
    
  2. CI/CD pipelines: Update SDK version references. If using global.json, update:

    {
      "sdk": {
        "version": "10.0.100"
      }
    }
    
  3. Environment variables renamed:

    • DOTNET_OPENSSL_VERSION_OVERRIDE replaces the old name
    • DOTNET_ICU_VERSION_OVERRIDE replaces the old name
    • NUGET_ENABLE_ENHANCED_HTTP_RETRY has been removed
  4. OpenSSL requirements: OpenSSL 1.1.1 or later is now required on Unix. OpenSSL cryptographic primitives are no longer supported on macOS.

  5. Solution file format: If dotnet new sln is used in scripts, note it now generates SLNX format. Pass --format sln if the old format is needed.

Step 6: Verify

  1. Run a full clean build: dotnet build --no-incremental
  2. Run all tests: dotnet test
  3. If the application is containerized, build and test the container image
  4. Smoke-test the application, paying special attention to:
    • Signal handling / graceful shutdown behavior
    • Background services startup ordering
    • Configuration binding with null values
    • Date/time handling with Sqlite
    • JSON serialization with polymorphic types
    • EF Core queries using .Contains() on collections
  5. Security review — verify that the migration has not weakened security controls:
    • TLS cipher validation logic is preserved after SslStream API migration (SYSLIB0058)
    • Obsolete properties containing sensitive data are excluded from serialization ([XmlIgnore], [JsonIgnore])
    • Input validation still rejects oversized URIs if Uri was used as a length gate
    • Exception handlers emit security-relevant telemetry (auth failures, access violations) before returning true
    • Connection strings set an explicit Application Name that does not leak version info
    • dotnet restore vulnerability audit findings are addressed, not suppressed
  6. Review the diff and ensure no unintended behavioral changes were introduced

Reference Documents

The references/ folder contains detailed breaking change information organized by technology area. Load only the references relevant to the project being migrated:

Reference file When to load
references/csharp-compiler-dotnet9to10.md Always (C# 14 compiler breaking changes — field keyword, extension keyword, span overloads)
references/core-libraries-dotnet9to10.md Always (applies to all .NET 10 projects)
references/sdk-msbuild-dotnet9to10.md Always (SDK and build tooling changes)
references/aspnet-core-dotnet9to10.md Project uses ASP.NET Core
references/efcore-dotnet9to10.md Project uses Entity Framework Core or Microsoft.Data.Sqlite
references/cryptography-dotnet9to10.md Project uses System.Security.Cryptography or X.509 certificates
references/extensions-hosting-dotnet9to10.md Project uses Generic Host, BackgroundService, or Microsoft.Extensions.Configuration
references/serialization-networking-dotnet9to10.md Project uses System.Text.Json, XmlSerializer, HttpClient, or networking APIs
references/winforms-wpf-dotnet9to10.md Project uses Windows Forms or WPF
references/containers-interop-dotnet9to10.md Project uses Docker containers, single-file publishing, or native interop (P/Invoke)
Files (skills)
  • references
    • aspnet-core-dotnet9to10.md 6.7 KB
      # ASP.NET Core 10 Breaking Changes
      
      These changes affect projects using ASP.NET Core (Microsoft.NET.Sdk.Web).
      
      ## Source-Incompatible Changes
      
      ### WebHostBuilder, IWebHost, and WebHost are obsolete
      
      The legacy `WebHostBuilder` and related APIs are now marked obsolete. Migrate to the modern hosting model:
      
      ```csharp
      // Before (.NET 9)
      var host = new WebHostBuilder()
          .UseKestrel()
          .UseStartup<Startup>()
          .Build();
      
      // After (.NET 10)
      var builder = WebApplication.CreateBuilder(args);
      // Configure services in builder.Services
      var app = builder.Build();
      // Configure middleware pipeline
      app.Run();
      ```
      
      If still using `Startup` classes, the `WebApplication` model supports them via `builder.Host.ConfigureWebHostDefaults(...)` or inline configuration.
      
      ### IActionContextAccessor and ActionContextAccessor are obsolete
      
      These types are obsolete. Access `ActionContext` through dependency injection or the `HttpContext` instead:
      ```csharp
      // Before
      services.AddSingleton<IActionContextAccessor, ActionContextAccessor>();
      
      // After — use IHttpContextAccessor or inject ActionContext directly in filters/middleware
      ```
      
      ### Deprecation of WithOpenApi extension method
      
      The `WithOpenApi()` extension method is deprecated. Use the built-in OpenAPI document generation in ASP.NET Core 10 instead.
      
      ### IncludeOpenAPIAnalyzers property and MVC API analyzers deprecated
      
      The `<IncludeOpenAPIAnalyzers>` MSBuild property is deprecated. Remove it from `.csproj` files. The analyzers are no longer needed with the new OpenAPI infrastructure.
      
      ### IPNetwork and ForwardedHeadersOptions.KnownNetworks are obsolete
      
      `IPNetwork` is obsolete. Use `System.Net.IPNetwork` (the new runtime type) and the new `KnownIpNetworks` property instead:
      ```csharp
      // Before
      app.UseForwardedHeaders(new ForwardedHeadersOptions
      {
          KnownNetworks = { new IPNetwork(IPAddress.Parse("10.0.0.0"), 8) }
      });
      
      // After — use KnownIpNetworks with the new System.Net.IPNetwork type
      app.UseForwardedHeaders(new ForwardedHeadersOptions
      {
          KnownIpNetworks = { new System.Net.IPNetwork(IPAddress.Parse("10.0.0.0"), 8) }
      });
      ```
      
      ### Razor runtime compilation is obsolete
      
      `AddRazorRuntimeCompilation()` is obsolete. Razor views and pages should be precompiled. For development, use hot reload (`dotnet watch`) instead.
      
      ### Microsoft.Extensions.ApiDescription.Client package deprecated
      
      The `Microsoft.Extensions.ApiDescription.Client` package is deprecated. Use the built-in OpenAPI client generation tooling instead.
      
      ### Microsoft.OpenApi 2.x breaking API changes
      
      `Microsoft.AspNetCore.OpenApi 10.0` depends on `Microsoft.OpenApi` v2.x (from [`microsoft/OpenAPI.NET`](https://github.com/microsoft/OpenAPI.NET) repo), which has significant breaking API changes from v1.x. Projects that customize OpenAPI document generation using transformers or directly manipulate OpenAPI models will need code changes. Note: no official v1→v2 migration guide exists; the repo has a [v3 upgrade guide](https://github.com/microsoft/OpenAPI.NET/blob/main/docs/upgrade-guide-3.md) only. These changes are also not listed on the ASP.NET Core 10 breaking changes page.
      
      **Namespace changes — `Microsoft.OpenApi.Models` and `Microsoft.OpenApi.Any` are completely removed:**
      
      All types previously in these namespaces (`OpenApiSchema`, `OpenApiParameter`, `OpenApiResponse`, `OpenApiSecurityScheme`, etc.) have moved to the root `Microsoft.OpenApi` namespace. Replace all `using Microsoft.OpenApi.Models;` and `using Microsoft.OpenApi.Any;` with `using Microsoft.OpenApi;`.
      
      ```csharp
      // Before (.NET 9 — Microsoft.OpenApi v1.x)
      using Microsoft.OpenApi.Any;
      using Microsoft.OpenApi.Models;
      
      // After (.NET 10 — Microsoft.OpenApi v2.x)
      using Microsoft.OpenApi;          // ALL model types are here now
      using System.Text.Json.Nodes;     // replaces OpenApiAny types
      ```
      
      **Key API changes:**
      
      1. **`OpenApiString` / `OpenApiAny` types removed** — Use `System.Text.Json.Nodes.JsonNode` instead:
         ```csharp
         // Before
         parameter.Schema.Example = new OpenApiString("1.0");
         // After
         parameter.Schema.Example = JsonNode.Parse("\"1.0\"");
         ```
      
      2. **`OpenApiSecurityScheme.Reference` replaced** — Use `OpenApiSecuritySchemeReference` directly:
         ```csharp
         // Before
         var scheme = new OpenApiSecurityScheme
         {
             Reference = new OpenApiReference { Type = ReferenceType.SecurityScheme, Id = "oauth2" }
         };
         // After
         var scheme = new OpenApiSecuritySchemeReference("oauth2", null);
         ```
      
      3. **Collections now nullable** — `operation.Parameters`, `operation.Responses`, `document.Components.SecuritySchemes`, and other collection properties may be null and must be checked or initialized:
         ```csharp
         operation.Responses ??= new OpenApiResponses();
         operation.Parameters?.FirstOrDefault(p => p.Name == "api-version");
         document.Components ??= new();
         document.Components.SecuritySchemes ??= new Dictionary<string, IOpenApiSecurityScheme>();
         ```
      
      4. **`OpenApiSchema.Nullable` removed** — OpenAPI 3.1 uses JSON Schema `type: ["string", "null"]` instead of `nullable: true`. Schema transformers that set `.Nullable = false` can be removed.
      
      5. **Security requirement values require `List<string>`** — Implicit conversion from `string[]` may not work:
         ```csharp
         // Before
         [oAuthScheme] = scopes
         // After
         [oAuthScheme] = scopes.ToList()
         ```
      
      6. **Interface types in some positions** — Some properties now use interfaces (e.g., `IOpenApiSecurityScheme` instead of `OpenApiSecurityScheme`). Check for compilation errors in transformer code.
      
      ## Behavioral Changes
      
      ### Cookie login redirects disabled for known API endpoints
      
      ASP.NET Core no longer redirects to login pages for requests to known API endpoints (e.g., those returning `ProblemDetails`). Instead, a `401` status code is returned directly. This is controlled by `IApiEndpointMetadata`, which is automatically applied to endpoints with `[ApiController]`, minimal API endpoints that read/write JSON, SignalR hubs, and endpoints returning `TypedResults`.
      
      This is generally the desired behavior for APIs, but may affect apps that relied on the redirect for API calls. To influence this behavior, you can manually add or check for `IApiEndpointMetadata` on specific endpoints.
      
      ### Exception diagnostics suppressed when TryHandleAsync returns true
      
      **Security consideration:** When `IExceptionHandler.TryHandleAsync` returns `true`, the exception diagnostics middleware no longer emits diagnostic events for that exception. If handled exceptions include security-relevant events (authentication failures, authorization violations, injection attempts), suppressing diagnostics could create blind spots in security monitoring and audit logging. Ensure your exception handler explicitly emits security-relevant telemetry before returning `true`.
      
    • containers-interop-dotnet9to10.md 3 KB
      # Containers, Interop, and Deployment Breaking Changes (.NET 10)
      
      These changes affect Docker containers, single-file apps, native interop (P/Invoke), and deployment scenarios.
      
      ## Containers
      
      ### Default .NET images use Ubuntu (Debian images discontinued)
      
      **Impact: Medium.** Default .NET container image tags now reference Ubuntu 24.04 (Noble Numbat) instead of Debian. Debian-based images are no longer provided for .NET 10.
      
      ```dockerfile
      # .NET 10 images — all Ubuntu-based
      FROM mcr.microsoft.com/dotnet/sdk:10.0 AS build
      FROM mcr.microsoft.com/dotnet/aspnet:10.0
      FROM mcr.microsoft.com/dotnet/runtime:10.0
      
      # Explicit Ubuntu tag (same as default)
      FROM mcr.microsoft.com/dotnet/sdk:10.0-noble
      ```
      
      **What to check:**
      - Dockerfile `RUN apt-get` commands — Ubuntu and Debian share `apt` but may differ in available packages and versions
      - Native library dependencies that were Debian-specific
      - Custom base image layers that assumed Debian
      - CI/CD scripts that referenced Debian-specific image tags
      
      **If you need Debian:** Create custom images following [Microsoft's guide for installing .NET in a Dockerfile](https://github.com/dotnet/dotnet-docker/blob/main/documentation/scenarios/installing-dotnet.md).
      
      ## Interop
      
      ### Single-file apps no longer look for native libraries in executable directory
      
      In single-file apps, the application directory is no longer automatically added to `NATIVE_DLL_SEARCH_DIRECTORIES`. The directory is only searched when `DllImportSearchPath.AssemblyDirectory` is included in the search paths (which is the default for P/Invokes without explicit search paths).
      
      **Breaking scenario:** P/Invokes with `[DefaultDllImportSearchPaths]` that explicitly exclude `AssemblyDirectory`:
      ```csharp
      // This no longer finds "lib" in the app directory:
      [DllImport("lib")]
      [DefaultDllImportSearchPaths(DllImportSearchPath.System32)]
      static extern void Method();
      
      // Fix: Add AssemblyDirectory to the search paths:
      [DllImport("lib")]
      [DefaultDllImportSearchPaths(DllImportSearchPath.System32 | DllImportSearchPath.AssemblyDirectory)]
      static extern void Method();
      ```
      
      For NativeAOT on non-Windows, the `rpath` is no longer set to the application directory. Add explicit linker arguments if needed.
      
      ### DllImportSearchPath.AssemblyDirectory only searches the assembly directory
      
      `DllImportSearchPath.AssemblyDirectory` now strictly searches only the assembly directory, not additional directories that were previously included.
      
      ### Casting IDispatchEx COM object to IReflect fails
      
      Casting a COM object that implements `IDispatchEx` to `IReflect` now fails. Use `dynamic` or explicit COM interop interfaces instead.
      
      ## Other Changes
      
      - **Globalization**: Environment variable renamed to `DOTNET_ICU_VERSION_OVERRIDE`
      - **Reflection**: `[DynamicallyAccessedMembers]` annotations on `IReflect.InvokeMember`, `Type.FindMembers` are more restrictive (new trim warnings possible)
      - **Reflection**: `Type.MakeGenericSignatureType` arguments validated more strictly
      - **VS Code**: `dotnet.acquire` API no longer always downloads latest — pin specific versions
      
    • core-libraries-dotnet9to10.md 4.8 KB
      # Core .NET Libraries Breaking Changes (.NET 10)
      
      These breaking changes affect all .NET 10 projects regardless of application type.
      
      ## Source-Incompatible Changes
      
      ### System.Linq.AsyncEnumerable included in core libraries
      
      .NET 10 adds `System.Linq.AsyncEnumerable` with full LINQ support for `IAsyncEnumerable<T>`, replacing the community `System.Linq.Async` NuGet package. Projects referencing `System.Linq.Async` will get ambiguity errors.
      
      **Fix:**
      - Remove the `System.Linq.Async` package reference, or upgrade to v7.0.0
      - If consumed transitively, suppress with `<ExcludeAssets>`:
        ```xml
        <PackageReference Include="System.Linq.Async" Version="6.0.1">
          <ExcludeAssets>compile</ExcludeAssets>
        </PackageReference>
        ```
      - Rename `SelectAwait` calls to `Select` where the new API uses a different name
      
      ### API obsoletions (SYSLIB0058–SYSLIB0062)
      
      | Diagnostic | What's obsolete | Replacement |
      |------------|----------------|-------------|
      | SYSLIB0058 | `SslStream.KeyExchangeAlgorithm`, `CipherAlgorithm`, `HashAlgorithm` and their strength properties | `SslStream.NegotiatedCipherSuite` — **Security note:** if existing code used these properties to reject weak ciphers (e.g., RC4, 3DES, NULL), ensure equivalent validation is preserved using `NegotiatedCipherSuite` |
      | SYSLIB0059 | `SystemEvents.EventsThreadShutdown` | `AppDomain.ProcessExit` |
      | SYSLIB0060 | `Rfc2898DeriveBytes` constructors | `Rfc2898DeriveBytes.Pbkdf2` static method |
      | SYSLIB0061 | `Queryable.MaxBy`/`MinBy` overloads with `IComparer<TSource>` | New overloads with `IComparer<TKey>` |
      | SYSLIB0062 | `XsltSettings.EnableScript` | N/A (XSLT scripting deprecated) |
      
      These use custom diagnostic IDs — suppressing `CS0618` does not suppress them.
      
      ### FilePatternMatch.Stem changed to non-nullable
      
      `FilePatternMatch.Stem` is now `string` instead of `string?`. Code checking for null may get warnings.
      
      ### Other source-incompatible changes (low impact)
      
      - `[DynamicallyAccessedMembers]` annotation removed from `DefaultValueAttribute` constructor (affects trimming annotations)
      - ARM64 SVE nonfaulting load intrinsics now require a mask parameter
      
      ## Behavioral Changes
      
      ### .NET runtime no longer provides default termination signal handlers
      
      **Impact: High for console/containerized apps without Generic Host.**
      
      On Unix, the runtime no longer registers SIGTERM/SIGHUP handlers. On Windows, `CTRL_SHUTDOWN_EVENT` and `CTRL_CLOSE_EVENT` are no longer handled. `AppDomain.ProcessExit` and `AssemblyLoadContext.Unloading` will NOT be raised on termination signals.
      
      - **ASP.NET Core and Generic Host apps are unaffected** (they register their own handlers)
      - **Console apps** must register handlers explicitly:
        ```csharp
        using var sigterm = PosixSignalRegistration.Create(
            PosixSignal.SIGTERM, _ => Environment.Exit(0));
        using var sighup = PosixSignalRegistration.Create(
            PosixSignal.SIGHUP, _ => Environment.Exit(0));
        ```
      
      ### C# 14 overload resolution with span parameters
      
      Methods with `ReadOnlySpan<T>` or `Span<T>` parameters now participate in type inference and extension method resolution. This can cause `MemoryExtensions.Contains` to bind instead of `Enumerable.Contains` inside Expression lambdas, causing runtime exceptions.
      
      **Fix:** Cast to `IEnumerable<T>`, use `.AsEnumerable()`, or call the static method explicitly:
      ```csharp
      // Fails — binds to MemoryExtensions.Contains
      M((array, num) => array.Contains(num));
      // Fix options:
      M((array, num) => ((IEnumerable<int>)array).Contains(num));
      M((array, num) => array.AsEnumerable().Contains(num));
      M((array, num) => Enumerable.Contains(array, num));
      ```
      
      ### BufferedStream.WriteByte no longer performs implicit flush
      
      `BufferedStream.WriteByte` no longer flushes when the buffer is full. Add explicit `Flush()` calls if your code relied on the implicit behavior.
      
      ### Consistent shift behavior in generic math
      
      Shift operations in generic math now behave consistently. If custom types relied on the previous inconsistent behavior, update them.
      
      ### Default trace context propagator updated to W3C standard
      
      The default `DistributedContextPropagator` now uses W3C Trace Context format. If your system relies on legacy propagation formats, configure the propagator explicitly.
      
      ### Other behavioral changes (lower impact)
      
      - `DriveInfo.DriveFormat` returns actual Linux filesystem type names (e.g., `ext4`) instead of generic values
      - `GnuTarEntry`/`PaxTarEntry` no longer include atime/ctime by default — set explicitly if needed
      - LDAP `DirectoryControl` parsing is more stringent — invalid data now throws
      - MacCatalyst versions normalized differently (affects .NET MAUI)
      - `ActivitySource.CreateActivity`/`StartActivity` sampling behavior changed
      - `[InlineArray]` structs can no longer have explicit `[StructLayout(Size = ...)]` — assembly fails to load
      - `Type.MakeGenericSignatureType` arguments validated more strictly
      
    • cryptography-dotnet9to10.md 2.8 KB
      # Cryptography Breaking Changes (.NET 10)
      
      These changes affect projects using `System.Security.Cryptography`, X.509 certificates, or OpenSSL.
      
      ## Source-Incompatible Changes
      
      ### MLDsa and SlhDsa 'SecretKey' members renamed to 'PrivateKey'
      
      All members containing `SecretKey` on `MLDsa` and `SlhDsa` types have been renamed to use `PrivateKey`. This affects methods and properties:
      
      ```csharp
      // Before
      int size = key.Algorithm.SecretKeySizeInBytes;
      byte[] output = new byte[size];
      key.ExportMLDsaSecretKey(output);
      key.ImportMLDsaSecretKey(data);
      
      // After
      int size = key.Algorithm.PrivateKeySizeInBytes;
      byte[] output = new byte[size];
      key.ExportMLDsaPrivateKey(output);
      key.ImportMLDsaPrivateKey(data);
      // Same pattern for SlhDsa: ExportSlhDsaSecretKey → ExportSlhDsaPrivateKey, etc.
      ```
      
      ### Rfc2898DeriveBytes constructors are obsolete (SYSLIB0060)
      
      All `Rfc2898DeriveBytes` constructors are now obsolete. Use the static `Rfc2898DeriveBytes.Pbkdf2` method instead:
      
      ```csharp
      // Before
      using var deriveBytes = new Rfc2898DeriveBytes(password, salt, iterations, HashAlgorithmName.SHA256);
      byte[] key = deriveBytes.GetBytes(32);
      
      // After
      byte[] key = Rfc2898DeriveBytes.Pbkdf2(password, salt, iterations, HashAlgorithmName.SHA256, 32);
      ```
      
      ### CoseSigner.Key can be null
      
      `CoseSigner.Key` is now nullable (`AsymmetricAlgorithm?`). Code that assumes it's non-null needs null checks:
      
      ```csharp
      // Before
      var algorithm = signer.Key.SignatureAlgorithm;
      
      // After
      var algorithm = signer.Key?.SignatureAlgorithm
          ?? throw new InvalidOperationException("Key is null");
      ```
      
      ### X509Certificate and PublicKey key parameters can be null
      
      `X509Certificate.GetKeyAlgorithmParameters()` and `PublicKey.EncodedParameters` can now return null. Add null checks where these values are consumed.
      
      ## Behavioral Changes
      
      ### OpenSSL 1.1.1 or later required on Unix
      
      .NET 10 requires OpenSSL 1.1.1+ on Unix systems. Older OpenSSL versions are no longer supported. Check with:
      ```bash
      openssl version
      ```
      
      ### OpenSSL cryptographic primitives aren't supported on macOS
      
      Using OpenSSL-specific cryptographic primitives on macOS is no longer supported. Use the platform's native cryptography (Apple Security framework) instead.
      
      ### X500DistinguishedName validation is stricter
      
      `X500DistinguishedName` now validates input more strictly. Malformed distinguished names that were previously accepted may now throw exceptions.
      
      ### CompositeMLDsa updated to draft-08
      
      The Composite ML-DSA implementation has been updated to align with draft-08. Key and signature formats from earlier drafts are incompatible.
      
      ### Environment variable renamed from CLR_OPENSSL_VERSION_OVERRIDE to DOTNET_OPENSSL_VERSION_OVERRIDE
      
      If you use `CLR_OPENSSL_VERSION_OVERRIDE` to specify the preferred OpenSSL library version on Linux, rename it to `DOTNET_OPENSSL_VERSION_OVERRIDE`.
      
    • csharp-compiler-dotnet9to10.md 4.9 KB
      # C# 14 Compiler Breaking Changes (.NET 10)
      
      These breaking changes are introduced by the Roslyn compiler shipping with the .NET 10 SDK. They affect all projects targeting `net10.0` (which uses C# 14 by default). These are maintained separately from the runtime breaking changes at: https://learn.microsoft.com/en-us/dotnet/csharp/whats-new/breaking-changes/compiler%20breaking%20changes%20-%20dotnet%2010
      
      ## Source-Incompatible Changes
      
      ### `field` keyword in property accessors
      
      **Impact: High.** `field` is now a contextual keyword inside property `get`, `set`, and `init` accessors (for the semi-auto properties feature).
      
      Two diagnostics apply:
      - **CS9258** (warning, VS 17.12+): `field` binds to the synthesized backing field instead of an existing member (e.g., a class field named `field`). Use `this.field` or `@field` to refer to the member.
      - **CS9272** (error, VS 17.14+): A local variable or nested-function parameter named `field` is **disallowed** inside a property accessor. Rename the variable or use `@field`.
      
      ```csharp
      // BREAKS — CS9272 error
      public object Property
      {
          get
          {
              int field = 0;       // error: 'field' is a keyword in a property accessor
              return @field;
          }
      }
      
      // Also BREAKS — CS9272 error
      public string Name
      {
          get
          {
              payload.TryGetProperty("field", out var field);  // error: local named 'field'
              return field.GetString();
          }
      }
      ```
      
      **Fix options:**
      1. Rename the variable: `var fieldValue = ...`, `var fieldElem = ...`
      2. Escape the identifier: `var @field = ...` (compiles but less readable)
      3. For class members named `field`, qualify with `this.field` or `@field`
      
      ### `extension` contextual keyword
      
      **Impact: Medium.** Starting in C# 14, `extension` is a contextual keyword for extension containers. Code that uses `extension` as a type name, constructor, or return type will break.
      
      ```csharp
      // BREAKS in C# 14
      class extension { }                    // type cannot be named "extension"
      using extension = SomeNamespace.Foo;   // alias cannot be named "extension"
      class C<extension> { }                 // type parameter cannot be named "extension"
      ```
      
      **Fix:** Rename the type, or escape as `@extension`.
      
      ### `Span<T>` and `ReadOnlySpan<T>` overloads applicable in more scenarios
      
      **Impact: Medium.** C# 14 introduces new built-in span conversions and type inference rules. Different overloads may be chosen, and new ambiguity errors can arise.
      
      Common patterns affected:
      ```csharp
      // Ambiguity — Assert.Equal<T>(T[], T[]) vs Assert.Equal<T>(ReadOnlySpan<T>, Span<T>)
      var x = new long[] { 1 };
      Assert.Equal([2], x);               // ambiguous
      Assert.Equal([2], x.AsSpan());      // fix
      
      // Enumerable.Reverse now resolves to MemoryExtensions.Reverse (in-place, returns void)
      int[] arr = [1, 2, 3];
      var reversed = arr.Reverse();        // BREAKS: resolves to Span extension, not Enumerable
      var reversed = Enumerable.Reverse(arr); // fix
      
      // ReadOnlySpan preferred over Span — MemoryMarshal.Cast may fail
      Span<ulong> y = MemoryMarshal.Cast<double, ulong>(x);          // BREAKS
      Span<ulong> y = MemoryMarshal.Cast<double, ulong>(x.AsSpan()); // fix
      
      // ArrayTypeMismatchException with covariant arrays
      string[] s = ["a"];
      object[] o = s;
      C.R(o);                // BREAKS at runtime: Span<T> ctor throws ArrayTypeMismatchException
      C.R(o.AsEnumerable()); // fix
      ```
      
      **Fix options:**
      - Add `.AsSpan()`, `.AsEnumerable()`, or explicit casts to disambiguate
      - Call static methods explicitly (e.g., `Enumerable.Reverse(arr)`)
      - API authors: use `[OverloadResolutionPriority]` attribute
      
      > **Note:** The span overload resolution change is also listed in the runtime breaking changes (`core-libraries.md`). This entry provides the full Roslyn-side detail.
      
      ### Other low-impact source changes
      
      - **`scoped` in lambda parameters**: Always treated as a modifier. If you have a ref struct type named `scoped`, escape as `@scoped`.
      - **`partial` as return type**: Cannot use a type named `partial` as a return type. Escape as `@partial`.
      
      ## Behavioral Changes
      
      ### Enumerator state set to "after" during disposal
      
      `MoveNext()` on a disposed enumerator now properly returns `false` without executing further user code. Previously, the state machine allowed resuming execution after disposal.
      
      ```csharp
      var enumerator = GetItems().GetEnumerator();
      enumerator.MoveNext();  // True, yields 1
      enumerator.Dispose();
      enumerator.MoveNext();  // now returns False (previously could continue)
      ```
      
      ### Diagnostics reported for pattern-based disposal in `foreach`
      
      Obsolete `DisposeAsync` methods on enumerator types are now reported in `await foreach`. Previously these diagnostics were silently ignored.
      
      ### Redundant pattern warning in `or` patterns
      
      The compiler now warns when the second pattern in a disjunctive `or` is redundant due to precedence:
      ```csharp
      _ = o is not null or 42;     // warning: pattern "42" is redundant
      _ = o is not int or string;  // warning: pattern "string" is redundant
      // Likely intended: is not (null or 42) / is not (int or string)
      ```
      
    • efcore-dotnet9to10.md 6.5 KB
      # Entity Framework Core 10 Breaking Changes
      
      These changes affect projects using EF Core or Microsoft.Data.Sqlite.
      
      ## Medium-Impact Changes
      
      ### EF tools require framework to be specified for multi-targeted projects
      
      When running EF tools on a project with `<TargetFrameworks>` (plural), you must now specify `--framework`:
      
      ```bash
      dotnet ef migrations add MyMigration --framework net10.0
      dotnet ef database update --framework net10.0
      ```
      
      Without this, you'll get: "The project targets multiple frameworks. Use the --framework option to specify which target framework to use."
      
      ## Low-Impact Changes
      
      ### Application Name injected into connection string
      
      EF now inserts an `Application Name` containing EF and SqlClient version info into connection strings that don't already have one. This changes the effective connection string, which can cause:
      - Separate connection pools when mixing EF and non-EF data access (e.g., Dapper)
      - Potential distributed transaction escalation within `TransactionScope`
      - **Information disclosure**: version strings are visible in `sys.dm_exec_sessions`, database server logs, and monitoring tools, potentially aiding attackers in fingerprinting your stack
      
      **Mitigation:** Explicitly set `Application Name` in your connection string to a value that does not reveal version information.
      
      ### SQL Server json data type used by default on Azure SQL and compatibility level 170
      
      For Azure SQL (`UseAzureSql`) or compatibility level ≥170, EF now maps JSON columns to the `json` data type instead of `nvarchar(max)`. A migration will be generated to alter existing columns.
      
      **Considerations:**
      - SQL Server does not support `DISTINCT` over JSON arrays — queries using it will fail
      - The column alteration is a non-trivial schema change
      
      **Mitigation:**
      ```csharp
      // Option 1: Set compatibility level below 170
      optionsBuilder.UseAzureSql(connStr, o => o.UseCompatibilityLevel(160));
      
      // Option 2: Explicitly set column type per property
      modelBuilder.Entity<Blog>()
          .PrimitiveCollection(b => b.Tags)
          .HasColumnType("nvarchar(max)");
      ```
      
      ### Parameterized collections now use multiple parameters by default
      
      `.Contains()` on collections now translates to `WHERE x IN (@p1, @p2, @p3)` instead of using `OPENJSON`. This gives the query planner cardinality information but may regress performance for large collections.
      
      **Mitigation:**
      ```csharp
      // Global: revert to JSON parameter mode
      optionsBuilder.UseSqlServer(connStr,
          o => o.UseParameterizedCollectionMode(ParameterTranslationMode.Parameter));
      
      // Per-query: use EF.Parameter() for JSON array translation
      var blogs = await context.Blogs
          .Where(b => EF.Parameter(ids).Contains(b.Id))
          .ToListAsync();
      ```
      
      ### ExecuteUpdateAsync now accepts a regular lambda
      
      `ExecuteUpdateAsync` now takes `Action<...>` instead of `Expression<Func<...>>` for column setters. Code that manually builds expression trees for dynamic setters will no longer compile but can be dramatically simplified:
      
      ```csharp
      // Before (.NET 9) — complex expression tree construction for dynamic setters
      Expression<Func<SetPropertyCalls<Blog>, SetPropertyCalls<Blog>>> setters =
          s => s.SetProperty(b => b.Views, 8);
      
      if (nameChanged)
      {
          var blogParameter = Expression.Parameter(typeof(Blog), "b");
          setters = Expression.Lambda<Func<SetPropertyCalls<Blog>, SetPropertyCalls<Blog>>>(
              Expression.Call(
                  instance: setters.Body,
                  methodName: nameof(SetPropertyCalls<Blog>.SetProperty),
                  typeArguments: [typeof(string)],
                  arguments:
                  [
                      Expression.Lambda<Func<Blog, string>>(
                          Expression.Property(blogParameter, nameof(Blog.Name)), blogParameter),
                      Expression.Constant("foo")
                  ]),
              setters.Parameters);
      }
      await context.Blogs.ExecuteUpdateAsync(setters);
      
      // After (.NET 10) — simple lambda with conditionals
      await context.Blogs.ExecuteUpdateAsync(s =>
      {
          s.SetProperty(b => b.Views, 8);
          if (nameChanged)
          {
              s.SetProperty(b => b.Name, "foo");
          }
      });
      ```
      
      ### Complex type column names are now uniquified
      
      If multiple complex types have properties with the same name, column names are now uniquified by appending a number. This may generate a migration that renames columns.
      
      **Mitigation:** Explicitly configure column names with `HasColumnName()`.
      
      ### Nested complex type properties use full path in column names
      
      `EntityType.Complex.NestedComplex.Property` is now mapped to `Complex_NestedComplex_Property` (was `NestedComplex_Property`). This generates a migration renaming columns.
      
      **Mitigation:** Use `HasColumnName()` to preserve old names.
      
      ### IDiscriminatorPropertySetConvention signature changed
      
      The method parameter changed from `IConventionEntityTypeBuilder` to `IConventionTypeBaseBuilder`. Update custom convention implementations.
      
      ### IRelationalCommandDiagnosticsLogger methods add logCommandText parameter
      
      Methods like `CommandReaderExecuting`, `CommandReaderExecuted`, `CommandScalarExecuting`, etc. now have an additional `string logCommandText` parameter containing redacted SQL for logging. Update custom implementations:
      
      ```csharp
      public InterceptionResult<DbDataReader> CommandReaderExecuting(
          IRelationalConnection connection,
          DbCommand command,
          DbContext context,
          Guid commandId,
          Guid connectionId,
          DateTimeOffset startTime,
          string logCommandText) // New parameter — redacted SQL for logging
      {
          // Use logCommandText for logging (may have constants redacted)
          // Use command.CommandText for actual SQL execution
      }
      ```
      
      ## Microsoft.Data.Sqlite Breaking Changes (All High Impact)
      
      ### GetDateTimeOffset without an offset now assumes UTC
      
      Previously, a textual timestamp without an offset (e.g., `2014-04-15 10:47:16`) was parsed using the local timezone. Now it's treated as UTC.
      
      ### Writing DateTimeOffset into REAL column now writes in UTC
      
      `DateTimeOffset` values written to REAL columns are now converted to UTC before writing. Previously the offset was ignored.
      
      ### GetDateTime with an offset now returns value in UTC
      
      `GetDateTime` on timestamps with offsets (e.g., `2014-04-15 10:47:16+02:00`) now returns the value converted to UTC with `DateTimeKind.Utc`. Previously it returned `DateTimeKind.Local`.
      
      **Mitigation for all three:**
      ```csharp
      // Temporary workaround to revert to .NET 9 behavior
      AppContext.SetSwitch("Microsoft.Data.Sqlite.Pre10TimeZoneHandling", true);
      ```
      
      Review all date/time handling code that reads from or writes to SQLite databases. The new behavior aligns with SQLite's convention that timestamps are UTC.
      
    • extensions-hosting-dotnet9to10.md 3.2 KB
      # Extensions and Hosting Breaking Changes (.NET 10)
      
      These changes affect projects using `Microsoft.Extensions.Hosting`, `BackgroundService`, `Microsoft.Extensions.Configuration`, and related libraries.
      
      ## Behavioral Changes
      
      ### BackgroundService.ExecuteAsync runs entirely on a background thread
      
      **Impact: Medium.** Previously, the synchronous code in `ExecuteAsync` before the first `await` ran on the main thread during startup, blocking other hosted services from starting. Now ALL of `ExecuteAsync` runs on a background thread.
      
      **If startup ordering matters:**
      
      ```csharp
      // Option 1: Move synchronous startup code to StartAsync
      public override async Task StartAsync(CancellationToken cancellationToken)
      {
          // This still runs synchronously during startup
          InitializeResources();
          await base.StartAsync(cancellationToken);
      }
      
      // Option 2: Use IHostedLifecycleService for fine-grained lifecycle control
      public class MyService : BackgroundService, IHostedLifecycleService
      {
          public Task StartingAsync(CancellationToken ct)
          {
              // runs before StartAsync
              return Task.CompletedTask;
          }
      
          public Task StartedAsync(CancellationToken ct)
          {
              // runs after StartAsync
              return Task.CompletedTask;
          }
          // ... other lifecycle methods
      }
      
      // Option 3: Move code to the constructor
      public MyService(ILogger<MyService> logger)
      {
          // Constructor code runs during DI resolution
      }
      ```
      
      ### Null values preserved in configuration
      
      **Impact: Medium.** JSON `null` values are now properly bound instead of being converted to empty strings or ignored. **Empty arrays (`[]`) are also now correctly bound as empty arrays instead of being ignored.**
      
      | Scenario | .NET 9 behavior | .NET 10 behavior |
      |----------|----------------|-----------------|
      | `"StringProperty": null` | Bound as `""` (empty string) | Bound as `null` (overwrites constructor default) |
      | `"IntProperty": null` | Ignored (kept constructor default) | Bound as `null` (if `int?`) |
      | `"Array": [null, null]` | Bound as `["", ""]` | Bound as `[null, null]` |
      | **`"Array": []`** | **Ignored (`null`)** | **Bound as empty array `[]`** |
      
      **If you need the old behavior:**
      - Replace `null` with `""` in JSON config files
      - Or remove `null` entries to skip binding
      
      ### Fix issues in GetKeyedService() and GetKeyedServices() with AnyKey
      
      `GetKeyedService()` and `GetKeyedServices()` with `AnyKey` now work correctly. If your code relied on the previous buggy behavior, update it.
      
      ### Message no longer duplicated in Console log output
      
      When using the JSON console logger, messages are no longer duplicated. If your log parsing relied on the duplicated format, update it.
      
      ### ProviderAliasAttribute moved to Microsoft.Extensions.Logging.Abstractions assembly
      
      `ProviderAliasAttribute` has moved assemblies. If you reference it directly by assembly-qualified name, update the reference. Source-incompatible for code using assembly-qualified type references.
      
      ### Removed DynamicallyAccessedMembers annotation from trim-unsafe configuration code
      
      The `[DynamicallyAccessedMembers]` annotation was removed from certain configuration APIs that are not trim-safe. Binary incompatible for code that relied on the annotation for trimming analysis.
      
    • sdk-msbuild-dotnet9to10.md 2.4 KB
      # SDK and MSBuild Breaking Changes (.NET 10)
      
      These changes affect the .NET SDK, CLI tooling, NuGet, and MSBuild behavior.
      
      ## Source-Incompatible Changes
      
      ### NU1510 raised for direct references pruned by NuGet
      
      If NuGet prunes a direct `PackageReference` because the package is already part of the shared framework, a `NU1510` warning is raised. Remove the explicit reference if it's provided by the framework.
      
      ### PackageReference without a version raises an error
      
      Every `<PackageReference>` must now have a `Version` attribute or use Central Package Management. Missing versions raise an error instead of resolving to the latest.
      
      ### Other source-incompatible changes
      
      - `dnx.ps1` file removed from .NET SDK — remove any references
      - File-level directives (`#r`, `#load`) no longer accept double-quoted paths — remove the quotes (e.g., `#r MyLib.dll` instead of `#r "MyLib.dll"`)
      - `project.json` no longer recognized by `dotnet restore` — use `PackageReference` format
      - NuGet packages with no runtime assets excluded from `deps.json`
      - `ToolCommandName` not set for non-tool packages
      - HTTP sources in `dotnet package list`/`dotnet package search` now error (use HTTPS)
      
      ## Behavioral Changes
      
      ### `dotnet new sln` defaults to SLNX file format
      
      `dotnet new sln` now generates an XML-based `.slnx` file instead of the classic `.sln` format:
      ```xml
      <Solution>
      </Solution>
      ```
      **Mitigation:** Use `dotnet new sln --format sln` for the classic format. Ensure your tooling (Visual Studio, Rider, etc.) supports SLNX.
      
      ### Other behavioral changes
      
      - `--interactive` defaults to `true` in user scenarios — use `--interactive false` in scripts
      - CLI diagnostic output now goes to stderr
      - Tool packages now include RID-specific content
      - Default workload management mode is 'workload sets' (not 'loose manifests')
      - `EnableDynamicNativeInstrumentation` defaults to false for code coverage
      - `dotnet package list` performs restore before listing
      - `dotnet tool install --local` creates manifest by default
      - `dotnet watch` logs to stderr instead of stdout
      - `PrunePackageReference` privatizes direct prunable references (`PrivateAssets="All"`)
      - SHA-1 fingerprints deprecated in `dotnet nuget sign` — use SHA-256
      - `MSBUILDCUSTOMBUILDEVENTWARNING` escape hatch removed
      - MSBuild custom culture resource handling changed
      - `NUGET_ENABLE_ENHANCED_HTTP_RETRY` env var removed (enhanced retry always on)
      - NuGet logs errors for invalid package IDs
      
    • serialization-networking-dotnet9to10.md 2.9 KB
      # Serialization and Networking Breaking Changes (.NET 10)
      
      These changes affect projects using System.Text.Json, XmlSerializer, HttpClient, and networking APIs.
      
      ## Serialization
      
      ### System.Text.Json checks for property name conflicts
      
      **Impact: Medium.** Polymorphic types with properties that conflict with metadata names (`$type`, `$id`, `$ref`, or custom `TypeDiscriminatorPropertyName`) now throw `InvalidOperationException` during serialization instead of producing invalid JSON.
      
      ```csharp
      // This now throws InvalidOperationException at serialization time:
      [JsonPolymorphic(TypeDiscriminatorPropertyName = "Type")]
      [JsonDerivedType(typeof(Dog), "dog")]
      public abstract class Animal
      {
          public abstract string Type { get; }  // Conflicts with "Type" discriminator
      }
      ```
      
      **Fix:** rename the conflicting property, or suppress it with `[JsonIgnore]`:
      
      ```csharp
      [JsonPolymorphic(TypeDiscriminatorPropertyName = "Type")]
      [JsonDerivedType(typeof(Dog), "dog")]
      public abstract class Animal
      {
          [JsonIgnore]
          public abstract string Type { get; }
      }
      ```
      
      ### XmlSerializer no longer ignores properties marked with ObsoleteAttribute
      
      **Security consideration:** Properties marked `[Obsolete]` are now included in XML serialization. Previously they were silently skipped. If obsolete properties contain sensitive data (e.g., deprecated password fields, legacy PII, or internal-only values), they will now appear in serialized output. Audit obsolete properties for sensitive data and mark them with `[XmlIgnore]` if they should not be serialized.
      
      ## Networking
      
      ### HTTP/3 support disabled by default with PublishTrimmed
      
      **When `<PublishTrimmed>true</PublishTrimmed>` or `<PublishAot>true</PublishAot>` is set, HTTP/3 support is completely disabled by default.** The HTTP/3 code is stripped by the trimmer. This is NOT a native library search issue — the HTTP/3 implementation code itself is removed.
      
      **Fix:** Add `<Http3Support>true</Http3Support>` to your `.csproj` to preserve HTTP/3 support when trimming:
      ```xml
      <PropertyGroup>
        <PublishTrimmed>true</PublishTrimmed>
        <Http3Support>true</Http3Support>
      </PropertyGroup>
      ```
      
      ### MailAddress enforces validation for consecutive dots
      
      `MailAddress` now rejects email addresses with consecutive dots (e.g., `user..name@example.com`). Previously these were accepted.
      
      ### Streaming HTTP responses enabled by default in browser HTTP clients
      
      In Blazor WebAssembly and other browser-based HTTP clients, streaming responses are now enabled by default. This may change how response content is buffered and consumed.
      
      ### Uri length limits removed
      
      **Security consideration:** The `Uri` class no longer enforces length limits. Previously, very long URIs could throw exceptions. If your application relied on `Uri` to reject excessively long input (as an input validation or sanitization gate), this removal may expose denial-of-service or resource exhaustion attack surface. Add explicit length validation before constructing `Uri` instances from untrusted input.
      
    • winforms-wpf-dotnet9to10.md 3.4 KB
      # Windows Forms and WPF Breaking Changes (.NET 10)
      
      These changes affect projects using Windows Forms (`<UseWindowsForms>true</UseWindowsForms>`) or WPF (`<UseWPF>true</UseWPF>`).
      
      ## Windows Forms
      
      ### Source-Incompatible Changes
      
      #### API obsoletions
      
      Several Windows Forms APIs have been marked obsolete with custom diagnostic IDs. Follow the guidance in the warning message for each.
      
      #### Applications referencing both WPF and WinForms must disambiguate MenuItem and ContextMenu types
      
      If a project references both WPF and WinForms, the `MenuItem` and `ContextMenu` types are ambiguous. Use fully qualified names:
      
      ```csharp
      // Before (ambiguous in .NET 10)
      var item = new MenuItem("File");
      
      // After
      var item = new System.Windows.Forms.MenuItem("File");
      // or
      var item = new System.Windows.Controls.MenuItem();
      ```
      
      #### Renamed parameter in HtmlElement.InsertAdjacentElement
      
      The parameter name in `HtmlElement.InsertAdjacentElement` has changed from `orientation`. Calls that use named arguments with `orientation` will no longer compile; update them to use positional arguments:
      
      ```csharp
      // Before — named arguments with old parameter name
      element.InsertAdjacentElement(orientation: HtmlElementInsertionOrientation.BeforeBegin, newElement: newElement);
      
      // After — use positional arguments
      element.InsertAdjacentElement(HtmlElementInsertionOrientation.BeforeBegin, newElement);
      ```
      
      ### Behavioral Changes
      
      #### TreeView checkbox image truncation
      
      The checkbox rendering in `TreeView` controls has been adjusted, which changes text positioning. Visual appearance may differ slightly.
      
      #### StatusStrip uses System RenderMode by default
      
      `StatusStrip` now uses the system render mode by default instead of a custom renderer. The visual appearance may change. Set `RenderMode` explicitly to restore the previous look.
      
      #### System.Drawing OutOfMemoryException changed to ExternalException
      
      **Important:** Some `System.Drawing` operations that previously threw `OutOfMemoryException` now throw `ExternalException` (from `System.Runtime.InteropServices` — NOT `ArgumentException`). This reflects the actual GDI+ error code. Update catch blocks:
      
      ```csharp
      // Before — only catching OutOfMemoryException
      try { /* drawing operation */ }
      catch (OutOfMemoryException) { /* handle */ }
      
      // After — catch ExternalException (the new exception type in .NET 10)
      try { /* drawing operation */ }
      catch (ExternalException) { /* handle */ }
      catch (OutOfMemoryException) { /* handle — for older runtimes */ }
      ```
      
      ## WPF
      
      ### Source-Incompatible / Behavioral Changes
      
      #### Empty ColumnDefinitions and RowDefinitions are disallowed
      
      Empty `<Grid.ColumnDefinitions/>` and `<Grid.RowDefinitions/>` elements in XAML now cause errors. Remove them if they don't contain any definitions:
      
      ```xml
      <!-- Before (now causes error) -->
      <Grid>
          <Grid.ColumnDefinitions/>
          <Grid.RowDefinitions/>
      </Grid>
      
      <!-- After -->
      <Grid>
          <!-- Only include definitions if you have columns/rows to define -->
      </Grid>
      ```
      
      #### Incorrect usage of DynamicResource causes application crash
      
      Incorrect `DynamicResource` usage that was silently ignored now causes crashes at runtime. Common issues:
      - Using `DynamicResource` where `StaticResource` is required (e.g., in non-dependency-property contexts)
      - Referencing resources that don't exist
      
      Audit all `DynamicResource` usage in XAML and ensure each reference points to a valid resource and is used in a context that supports dynamic resources.
      
  • SKILL.md 18.9 KB
    ---
    name: migrate-dotnet9-to-dotnet10
    description: >
      Migrate a .NET 9 project or solution to .NET 10 and resolve all breaking changes.
      USE FOR: upgrading TargetFramework from net9.0 to net10.0, fixing build errors
      after updating the .NET 10 SDK, resolving source and behavioral changes in
      .NET 10 / C# 14 / ASP.NET Core 10 / EF Core 10, updating Dockerfiles for
      Debian-to-Ubuntu base images, resolving obsoletion warnings
      (SYSLIB0058-SYSLIB0062), adapting to SDK/NuGet changes (NU1510,
      PrunePackageReference), migrating System.Linq.Async to built-in
      AsyncEnumerable, fixing OpenApi v2 API changes, cryptography renames, and
      C# 14 compiler changes (field keyword, extension keyword, span overloads).
      DO NOT USE FOR: .NET Framework migrations, upgrading from .NET 8 or earlier
      (use migrate-dotnet8-to-dotnet9 first), greenfield .NET 10 projects, or
      cosmetic modernization.
      LOADS REFERENCES: csharp-compiler, core-libraries, sdk-msbuild (always);
      aspnet-core, efcore, cryptography, extensions-hosting,
      serialization-networking, winforms-wpf, containers-interop (selective).
    license: MIT
    ---
    
    # .NET 9 → .NET 10 Migration
    
    Migrate a .NET 9 project or solution to .NET 10, systematically resolving all breaking changes. The outcome is a project targeting `net10.0` that builds cleanly, passes tests, and accounts for every behavioral, source-incompatible, and binary-incompatible change introduced in the .NET 10 release.
    
    ## When to Use
    
    - Upgrading `TargetFramework` from `net9.0` to `net10.0`
    - Resolving build errors or new warnings after updating the .NET 10 SDK
    - Adapting to behavioral changes in .NET 10 runtime, ASP.NET Core 10, or EF Core 10
    - Updating CI/CD pipelines, Dockerfiles, or deployment scripts for .NET 10
    - Migrating from the community `System.Linq.Async` package to the built-in `System.Linq.AsyncEnumerable`
    
    ## When Not to Use
    
    - The project already targets `net10.0` and builds cleanly — migration is done
    - Upgrading from .NET 8 or earlier — use the `migrate-dotnet8-to-dotnet9` skill first to reach `net9.0`, then return to this skill for the `net9.0` → `net10.0` migration
    - Migrating from .NET Framework — that is a separate, larger effort
    - Greenfield projects that start on .NET 10 (no migration needed)
    
    ## Inputs
    
    | Input | Required | Description |
    |-------|----------|-------------|
    | Project or solution path | Yes | The `.csproj`, `.sln`, or `.slnx` entry point to migrate |
    | Build command | No | How to build (e.g., `dotnet build`, a repo build script). Auto-detect if not provided |
    | Test command | No | How to run tests (e.g., `dotnet test`). Auto-detect if not provided |
    | Project type hints | No | Whether the project uses ASP.NET Core, EF Core, WinForms, WPF, containers, etc. Auto-detect from PackageReferences and SDK attributes if not provided |
    
    ## Workflow
    
    > **Answer directly from the loaded reference documents.** Do not search the filesystem or fetch web pages for breaking change information — the references contain the authoritative details. Focus on identifying which breaking changes apply and providing concrete fixes. **Exception:** If you suspect a security vulnerability (CVE) may apply to the project's dependencies, check for published security advisories — the reference documents may not cover post-publication CVEs.
    >
    > **Commit strategy:** Commit at each logical boundary — after updating the TFM (Step 2), after resolving build errors (Step 3), after addressing behavioral changes (Step 4), and after updating infrastructure (Step 5). This keeps each commit focused and reviewable.
    
    ### Step 1: Assess the project
    
    1. Identify how the project is built and tested. Look for build scripts, `.sln`/`.slnx` files, or individual `.csproj` files.
    2. Run `dotnet --version` to confirm the .NET 10 SDK is installed. If it is not, stop and inform the user.
    3. Determine which technology areas the project uses by examining:
       - **SDK attribute**: `Microsoft.NET.Sdk.Web` → ASP.NET Core; `Microsoft.NET.Sdk.WindowsDesktop` with `<UseWPF>` or `<UseWindowsForms>` → WPF/WinForms
       - **PackageReferences**: `Microsoft.EntityFrameworkCore.*` → EF Core; `Microsoft.Data.Sqlite` → Sqlite; `Microsoft.Extensions.Hosting` → Generic Host / BackgroundService
       - **Dockerfile presence** → Container changes relevant
       - **P/Invoke or native interop usage** → Interop changes relevant
       - **`System.Linq.Async` package reference** → AsyncEnumerable migration needed
       - **`System.Text.Json` usage with polymorphism** → Serialization changes relevant
    4. Record which reference documents are relevant (see the reference loading table in Step 3).
    5. Do a **clean build** (`dotnet build --no-incremental` or delete `bin`/`obj`) on the current `net9.0` target to establish a clean baseline. Record any pre-existing warnings.
    
    ### Step 2: Update the Target Framework
    
    1. In each `.csproj` (or `Directory.Build.props` if centralized), change:
       ```xml
       <TargetFramework>net9.0</TargetFramework>
       ```
       to:
       ```xml
       <TargetFramework>net10.0</TargetFramework>
       ```
       For multi-targeted projects, add `net10.0` to `<TargetFrameworks>` or replace `net9.0`.
    
    2. Update all `Microsoft.Extensions.*`, `Microsoft.AspNetCore.*`, `Microsoft.EntityFrameworkCore.*`, and other Microsoft package references to their 10.0.x versions. If using Central Package Management (`Directory.Packages.props`), update versions there.
    
    3. Run `dotnet restore`. Watch for:
       - **NU1510**: Direct references pruned by NuGet — the package may be included in the shared framework now. Remove the explicit `<PackageReference>` if so.
       - **PackageReference without a version now raises an error** — every `<PackageReference>` must have a `Version` (or use CPM).
       - **NuGet auditing of transitive packages** (`dotnet restore` now audits transitive deps) — review any new vulnerability warnings.
    
    4. Run a clean build. Collect all errors and new warnings. These will be addressed in Step 3.
    
    ### Step 3: Resolve build errors and source-incompatible changes
    
    Work through compilation errors and new warnings systematically. Load the appropriate reference documents based on the project type:
    
    | If the project uses… | Load reference |
    |-----------------------|----------------|
    | Any .NET 10 project | `references/csharp-compiler-dotnet9to10.md` |
    | Any .NET 10 project | `references/core-libraries-dotnet9to10.md` |
    | Any .NET 10 project | `references/sdk-msbuild-dotnet9to10.md` |
    | ASP.NET Core | `references/aspnet-core-dotnet9to10.md` |
    | Entity Framework Core | `references/efcore-dotnet9to10.md` |
    | Cryptography APIs | `references/cryptography-dotnet9to10.md` |
    | Microsoft.Extensions.Hosting, BackgroundService, configuration | `references/extensions-hosting-dotnet9to10.md` |
    | System.Text.Json, XmlSerializer, HttpClient, MailAddress, Uri | `references/serialization-networking-dotnet9to10.md` |
    | Windows Forms or WPF | `references/winforms-wpf-dotnet9to10.md` |
    | Docker containers, single-file apps, native interop | `references/containers-interop-dotnet9to10.md` |
    
    **Common source-incompatible changes to check for:**
    
    1. **`System.Linq.Async` conflicts** — Remove the `System.Linq.Async` package reference or upgrade to v7.0.0. If consumed transitively, add `<ExcludeAssets>compile</ExcludeAssets>`. Rename `SelectAwait` calls to `Select` where needed.
    
    2. **New obsoletion warnings (SYSLIB0058–SYSLIB0062)**:
       - `SYSLIB0058`: Replace `SslStream.KeyExchangeAlgorithm`/`CipherAlgorithm`/`HashAlgorithm` with `NegotiatedCipherSuite` — if the old properties were used to reject weak TLS ciphers, preserve equivalent validation logic using the new API
       - `SYSLIB0059`: Replace `SystemEvents.EventsThreadShutdown` with `AppDomain.ProcessExit`
       - `SYSLIB0060`: Replace `Rfc2898DeriveBytes` constructors with `Rfc2898DeriveBytes.Pbkdf2`
       - `SYSLIB0061`: Replace `Queryable.MaxBy`/`MinBy` overloads taking `IComparer<TSource>` with ones taking `IComparer<TKey>`
       - `SYSLIB0062`: Replace `XsltSettings.EnableScript` usage
    
    3. **C# 14 `field` keyword in property accessors** — The identifier `field` is now a contextual keyword inside property `get`/`set`/`init` accessors. Local variables named `field` cause CS9272 (error). Class members named `field` referenced without `this.` cause CS9258 (warning). Fix by renaming (e.g., `fieldValue`) or escaping with `@field`. See `references/csharp-compiler-dotnet9to10.md`.
    
    4. **C# 14 `extension` contextual keyword** — Types, aliases, or type parameters named `extension` are disallowed. Rename or escape with `@extension`.
    
    5. **C# 14 overload resolution with span parameters** — Expression trees containing `.Contains()` on arrays may now bind to `MemoryExtensions.Contains` instead of `Enumerable.Contains`. `Enumerable.Reverse` on arrays may resolve to the in-place `Span` extension. Fix by casting to `IEnumerable<T>`, using `.AsEnumerable()`, or explicit static invocations. See `references/csharp-compiler-dotnet9to10.md` for full details.
    
    6. **ASP.NET Core obsoletions** (if applicable):
       - `WebHostBuilder`, `IWebHost`, `WebHost` are obsolete — migrate to `Host.CreateDefaultBuilder` or `WebApplication.CreateBuilder`
       - `IActionContextAccessor` / `ActionContextAccessor` obsolete
       - `WithOpenApi` extension method deprecated
       - `IncludeOpenAPIAnalyzers` property deprecated
       - `IPNetwork` and `ForwardedHeadersOptions.KnownNetworks` obsolete
       - Razor runtime compilation is obsolete
       - `Microsoft.Extensions.ApiDescription.Client` package deprecated
       - **`Microsoft.OpenApi` v2.x breaking changes** — `Microsoft.AspNetCore.OpenApi 10.0` pulls in `Microsoft.OpenApi` v2.x which restructures namespaces and models. `OpenApiString`/`OpenApiAny` types are removed (use `JsonNode`), `OpenApiSecurityScheme.Reference` replaced by `OpenApiSecuritySchemeReference`, collections on OpenAPI model objects may be null, and `OpenApiSchema.Nullable` is removed. See `references/aspnet-core-dotnet9to10.md` for migration patterns.
    
    7. **SDK changes**:
       - `dotnet new sln` now defaults to SLNX format — use `--format sln` if the old format is needed
       - Double quotes in file-level directives are disallowed
       - `dnx.ps1` removed from .NET SDK
       - `project.json` no longer supported in `dotnet restore`
    
    8. **EF Core source changes** (if applicable) — See `references/efcore-dotnet9to10.md` for:
       - `ExecuteUpdateAsync` now accepts a regular lambda (expression tree construction code must be rewritten)
       - `IDiscriminatorPropertySetConvention` signature changed
       - `IRelationalCommandDiagnosticsLogger` methods add `logCommandText` parameter
    
    9. **WinForms/WPF source changes** (if applicable):
       - Applications referencing both WPF and WinForms must disambiguate `MenuItem` and `ContextMenu` types
       - Renamed parameter in `HtmlElement.InsertAdjacentElement`
       - Empty `ColumnDefinitions` and `RowDefinitions` are disallowed in WPF
    
    10. **Cryptography source changes** (if applicable):
       - `MLDsa` and `SlhDsa` members renamed from `SecretKey` to `PrivateKey` (e.g., `ExportMLDsaSecretKey` → `ExportMLDsaPrivateKey`, `SecretKeySizeInBytes` → `PrivateKeySizeInBytes`)
       - `Rfc2898DeriveBytes` constructors are obsolete (SYSLIB0060) — replace with static `Rfc2898DeriveBytes.Pbkdf2(password, salt, iterations, hashAlgorithm, outputLength)`
       - `CoseSigner.Key` can now be null — check for null before use
       - `X509Certificate.GetKeyAlgorithmParameters()` and `PublicKey.EncodedParameters` can return null
       - Environment variable renamed from `CLR_OPENSSL_VERSION_OVERRIDE` to `DOTNET_OPENSSL_VERSION_OVERRIDE`
    
    Build again after each batch of fixes. Repeat until the build is clean.
    
    ### Step 4: Address behavioral changes
    
    Behavioral changes do not cause build errors but may change runtime behavior. Review each applicable item and determine whether the previous behavior was relied upon.
    
    **High-impact behavioral changes (check first):**
    
    1. **SIGTERM signal handling removed** — The .NET runtime no longer registers default SIGTERM handlers. If you rely on `AppDomain.ProcessExit` or `AssemblyLoadContext.Unloading` being raised on SIGTERM:
       - ASP.NET Core and Generic Host apps are unaffected (they register their own handlers)
       - Console apps and containerized apps without Generic Host must register `PosixSignalRegistration.Create(PosixSignal.SIGTERM, _ => Environment.Exit(0))` explicitly
    
    2. **BackgroundService.ExecuteAsync runs entirely on a background thread** — The synchronous portion before the first `await` no longer blocks startup. If startup ordering matters, move that code to `StartAsync` or the constructor, or implement `IHostedLifecycleService`.
    
    3. **Configuration null values are now preserved** — JSON `null` values are no longer converted to empty strings. Properties initialized with non-default values will be overwritten with `null`. Review configuration binding code.
    
    4. **Microsoft.Data.Sqlite DateTimeOffset changes** (all High impact):
       - `GetDateTimeOffset` without an offset now assumes UTC (previously assumed local)
       - Writing `DateTimeOffset` into REAL columns now converts to UTC first
       - `GetDateTime` with an offset now returns UTC with `DateTimeKind.Utc`
       - Mitigation: `AppContext.SetSwitch("Microsoft.Data.Sqlite.Pre10TimeZoneHandling", true)` as a temporary workaround
    
    5. **EF Core parameterized collections** — `.Contains()` on collections now uses multiple scalar parameters instead of JSON/OPENJSON. May affect query performance for large collections. Mitigation: `UseParameterizedCollectionMode(ParameterTranslationMode.Parameter)` to revert.
    
    6. **EF Core JSON data type on Azure SQL** — Azure SQL and compatibility level ≥170 now use the `json` data type instead of `nvarchar(max)`. A migration will be generated to alter existing columns. Mitigation: set compatibility level to 160 or use `HasColumnType("nvarchar(max)")` explicitly.
    
    7. **System.Text.Json property name conflict validation** — Polymorphic types with properties conflicting with metadata names (`$type`, `$id`, `$ref`) now throw `InvalidOperationException`. Add `[JsonIgnore]` to conflicting properties.
    
    **Other behavioral changes to review:**
    
    - `BufferedStream.WriteByte` no longer implicitly flushes — add explicit `Flush()` calls if needed
    - Default trace context propagator updated to W3C standard
    - `DriveInfo.DriveFormat` returns actual Linux filesystem type names
    - LDAP `DirectoryControl` parsing is more stringent
    - Default .NET container images switched from Debian to Ubuntu (Debian images no longer shipped)
    - Single-file apps no longer look for native libraries in executable directory by default
    - `DllImportSearchPath.AssemblyDirectory` only searches the assembly directory
    - `MailAddress` enforces validation for consecutive dots
    - Streaming HTTP responses enabled by default in browser HTTP clients
    - `Uri` length limits removed — add explicit length validation if `Uri` was used to reject oversized input from untrusted sources
    - Cookie login redirects disabled for known API endpoints (ASP.NET Core)
    - `XmlSerializer` no longer ignores `[Obsolete]` properties — audit obsolete properties for sensitive data and add `[XmlIgnore]` to prevent unintended data exposure
    - `dotnet restore` audits transitive packages
    - `dotnet watch` logs to stderr instead of stdout
    - `dotnet` CLI commands log non-command-relevant data to stderr
    - Various NuGet behavioral changes (see `references/sdk-msbuild-dotnet9to10.md`)
    - `StatusStrip` uses System RenderMode by default (WinForms)
    - `TreeView` checkbox image truncation fix (WinForms)
    - `DynamicResource` incorrect usage causes crash (WPF)
    
    ### Step 5: Update infrastructure
    
    1. **Dockerfiles**: Update base images. Default tags now use Ubuntu instead of Debian. Debian images are no longer shipped for .NET 10.
       ```dockerfile
       # Before
       FROM mcr.microsoft.com/dotnet/sdk:9.0 AS build
       FROM mcr.microsoft.com/dotnet/aspnet:9.0
       # After
       FROM mcr.microsoft.com/dotnet/sdk:10.0 AS build
       FROM mcr.microsoft.com/dotnet/aspnet:10.0
       ```
    
    2. **CI/CD pipelines**: Update SDK version references. If using `global.json`, update:
       ```json
       {
         "sdk": {
           "version": "10.0.100"
         }
       }
       ```
    
    3. **Environment variables renamed**:
       - `DOTNET_OPENSSL_VERSION_OVERRIDE` replaces the old name
       - `DOTNET_ICU_VERSION_OVERRIDE` replaces the old name
       - `NUGET_ENABLE_ENHANCED_HTTP_RETRY` has been removed
    
    4. **OpenSSL requirements**: OpenSSL 1.1.1 or later is now required on Unix. OpenSSL cryptographic primitives are no longer supported on macOS.
    
    5. **Solution file format**: If `dotnet new sln` is used in scripts, note it now generates SLNX format. Pass `--format sln` if the old format is needed.
    
    ### Step 6: Verify
    
    1. Run a full clean build: `dotnet build --no-incremental`
    2. Run all tests: `dotnet test`
    3. If the application is containerized, build and test the container image
    4. Smoke-test the application, paying special attention to:
       - Signal handling / graceful shutdown behavior
       - Background services startup ordering
       - Configuration binding with null values
       - Date/time handling with Sqlite
       - JSON serialization with polymorphic types
       - EF Core queries using `.Contains()` on collections
    5. **Security review** — verify that the migration has not weakened security controls:
       - TLS cipher validation logic is preserved after `SslStream` API migration (SYSLIB0058)
       - Obsolete properties containing sensitive data are excluded from serialization (`[XmlIgnore]`, `[JsonIgnore]`)
       - Input validation still rejects oversized URIs if `Uri` was used as a length gate
       - Exception handlers emit security-relevant telemetry (auth failures, access violations) before returning `true`
       - Connection strings set an explicit `Application Name` that does not leak version info
       - `dotnet restore` vulnerability audit findings are addressed, not suppressed
    6. Review the diff and ensure no unintended behavioral changes were introduced
    
    ## Reference Documents
    
    The `references/` folder contains detailed breaking change information organized by technology area. Load only the references relevant to the project being migrated:
    
    | Reference file | When to load |
    |----------------|-------------|
    | `references/csharp-compiler-dotnet9to10.md` | Always (C# 14 compiler breaking changes — field keyword, extension keyword, span overloads) |
    | `references/core-libraries-dotnet9to10.md` | Always (applies to all .NET 10 projects) |
    | `references/sdk-msbuild-dotnet9to10.md` | Always (SDK and build tooling changes) |
    | `references/aspnet-core-dotnet9to10.md` | Project uses ASP.NET Core |
    | `references/efcore-dotnet9to10.md` | Project uses Entity Framework Core or Microsoft.Data.Sqlite |
    | `references/cryptography-dotnet9to10.md` | Project uses System.Security.Cryptography or X.509 certificates |
    | `references/extensions-hosting-dotnet9to10.md` | Project uses Generic Host, BackgroundService, or Microsoft.Extensions.Configuration |
    | `references/serialization-networking-dotnet9to10.md` | Project uses System.Text.Json, XmlSerializer, HttpClient, or networking APIs |
    | `references/winforms-wpf-dotnet9to10.md` | Project uses Windows Forms or WPF |
    | `references/containers-interop-dotnet9to10.md` | Project uses Docker containers, single-file publishing, or native interop (P/Invoke) |
    

Comments (0)

Sign in to join the conversation.

No comments yet.

Reviews (0)

No reviews yet.

Related