mirror of
https://github.com/dotnet/skills.git
synced 2026-09-20 09:49:54 +08:00
Add migrate-dotnet9-to-dotnet10 skill (#180)
* Add migrate-dotnet9-to-dotnet10 skill Adds a new migration skill that guides upgrading .NET 9 projects to .NET 10, covering TFM updates, NuGet package upgrades, and source-breaking changes across the full .NET 10 surface area. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Address PR review feedback: fix eval/reference consistency and CODEOWNERS - Fix ExecuteUpdateAsync rubric: use 'regular lambda/delegate instead of expression tree' instead of incorrect 'Func instead of Expression' - Fix config null binding eval prompt: .NET 9 bound null as empty string, not preserving constructor default - Clarify .NET 10 null binding overwrites constructor default in reference - Add @ViktorHofer as CODEOWNER for migrate-dotnet9-to-dotnet10 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Suffix reference filenames with -dotnet9to10 to avoid collisions Rename all reference files under migrate-dotnet9-to-dotnet10/references/ from e.g. efcore.md to efcore-dotnet9to10.md so they don't collide with reference files from other version migration skills. Update all references in SKILL.md to match. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Address BrennanConroy review: ForwardedHeaders KnownIpNetworks example and IApiEndpointMetadata guidance - Show KnownIpNetworks property with UseForwardedHeaders context in example - Add IApiEndpointMetadata explanation for cookie login redirect behavior - Link to aspnetcore#62883 for details on influencing the behavior Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Shorten skill description to fit 1024-char limit Reduce description from 1,370 to 1,017 characters by trimming redundant phrasing while preserving all key search terms. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Update CODEOWNERS --------- Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This commit is contained in:
@@ -30,6 +30,8 @@
|
||||
/plugins/dotnet-upgrade/skills/dotnet-aot-compat/ @agocke @dotnet/appmodel
|
||||
/tests/dotnet-upgrade/dotnet-aot-compat/ @agocke @dotnet/appmodel
|
||||
|
||||
/plugins/dotnet-upgrade/skills/migrate-dotnet9-to-dotnet10/ @danmoseley @dotnet/compat
|
||||
/tests/dotnet-upgrade/migrate-dotnet9-to-dotnet10/ @danmoseley @dotnet/compat
|
||||
/plugins/dotnet-upgrade/skills/migrate-dotnet10-to-dotnet11/ @danmoseley @dotnet/compat
|
||||
/tests/dotnet-upgrade/migrate-dotnet10-to-dotnet11/ @danmoseley @dotnet/compat
|
||||
/plugins/dotnet-upgrade/skills/migrate-dotnet8-to-dotnet9/ @danmoseley @dotnet/compat
|
||||
|
||||
@@ -0,0 +1,273 @@
|
||||
---
|
||||
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).
|
||||
---
|
||||
|
||||
# .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) |
|
||||
+140
@@ -0,0 +1,140 @@
|
||||
# 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`.
|
||||
+63
@@ -0,0 +1,63 @@
|
||||
# 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
|
||||
+93
@@ -0,0 +1,93 @@
|
||||
# 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
|
||||
+79
@@ -0,0 +1,79 @@
|
||||
# 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`.
|
||||
+118
@@ -0,0 +1,118 @@
|
||||
# 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)
|
||||
```
|
||||
+157
@@ -0,0 +1,157 @@
|
||||
# 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.
|
||||
+75
@@ -0,0 +1,75 @@
|
||||
# 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.
|
||||
+50
@@ -0,0 +1,50 @@
|
||||
# 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 — use single quotes
|
||||
- `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
|
||||
+56
@@ -0,0 +1,56 @@
|
||||
# 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 property, or add [JsonIgnore]:
|
||||
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.
|
||||
+91
@@ -0,0 +1,91 @@
|
||||
# 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.
|
||||
@@ -0,0 +1,628 @@
|
||||
scenarios:
|
||||
# --- Scenario 1: Core libraries (AsyncEnumerable, SIGTERM, BufferedStream, trace propagation) ---
|
||||
- name: "Console app with System.Linq.Async, SIGTERM, and BufferedStream"
|
||||
prompt: |
|
||||
I have a .NET 9 console app (no Generic Host) that:
|
||||
- References System.Linq.Async 6.0.1 for IAsyncEnumerable LINQ queries
|
||||
- Uses AppDomain.ProcessExit to flush logs and clean up when SIGTERM is received in Docker
|
||||
- Has a custom stream pipeline that writes byte-by-byte to a BufferedStream and expects automatic flushing when the buffer fills
|
||||
- Uses DistributedContextPropagator for trace context propagation in a custom format (not W3C)
|
||||
What breaks when migrating to .NET 10?
|
||||
assertions:
|
||||
- type: "output_matches"
|
||||
pattern: "(System\\.Linq\\.Async|AsyncEnumerable)"
|
||||
- type: "output_matches"
|
||||
pattern: "(SIGTERM|PosixSignal|signal|ProcessExit)"
|
||||
- type: "output_matches"
|
||||
pattern: "(BufferedStream|WriteByte|[Ff]lush)"
|
||||
rubric:
|
||||
- "Identifies System.Linq.Async conflict with built-in AsyncEnumerable; recommends removing the package, upgrading to v7, or using ExcludeAssets"
|
||||
- "Explains the runtime no longer registers SIGTERM handlers, so AppDomain.ProcessExit won't fire; recommends PosixSignalRegistration.Create"
|
||||
- "Warns that BufferedStream.WriteByte no longer implicitly flushes and recommends adding explicit Flush() calls"
|
||||
- "Mentions the default trace context propagator changed to W3C standard"
|
||||
timeout: 240
|
||||
|
||||
# --- Scenario 2: C# 14 span overloads + Expression trees ---
|
||||
- name: "Expression tree code broken by C# 14 span overload resolution"
|
||||
prompt: |
|
||||
I'm migrating a .NET 9 library to .NET 10. It builds LINQ expression trees that call .Contains() on int[] arrays:
|
||||
```csharp
|
||||
Expression<Func<int[], int, bool>> expr = (array, num) => array.Contains(num);
|
||||
expr.Compile(preferInterpretation: true)(new[] { 1, 2, 3 }, 2);
|
||||
```
|
||||
After upgrading to .NET 10 / C# 14, this throws at runtime. What happened and how do I fix it?
|
||||
assertions:
|
||||
- type: "output_matches"
|
||||
pattern: "(span|Span|MemoryExtensions|overload)"
|
||||
- type: "output_matches"
|
||||
pattern: "(IEnumerable|AsEnumerable|Enumerable\\.Contains)"
|
||||
rubric:
|
||||
- "Explains that C# 14 span conversions cause MemoryExtensions.Contains to bind instead of Enumerable.Contains for array.Contains() in expressions"
|
||||
- "Provides at least one concrete fix: casting to IEnumerable<int>, using .AsEnumerable(), or calling Enumerable.Contains explicitly"
|
||||
timeout: 180
|
||||
|
||||
# --- Scenario 3: ASP.NET Core obsoletions + OpenAPI + cookie auth ---
|
||||
- name: "ASP.NET Core app with WebHostBuilder, OpenAPI, and forwarded headers"
|
||||
prompt: |
|
||||
I'm upgrading a .NET 9 ASP.NET Core web API. The codebase:
|
||||
- Uses WebHostBuilder in Program.cs (the legacy hosting model)
|
||||
- Calls .WithOpenApi() on minimal API endpoints
|
||||
- Has <IncludeOpenAPIAnalyzers>true</IncludeOpenAPIAnalyzers> in the csproj
|
||||
- References Microsoft.Extensions.ApiDescription.Client for NSwag code generation
|
||||
- Uses IActionContextAccessor registered as a singleton
|
||||
- Uses IPNetwork in ForwardedHeadersOptions.KnownNetworks
|
||||
- Uses AddRazorRuntimeCompilation() for dev-time Razor editing
|
||||
- Has API endpoints that currently get redirected to the login page on 401
|
||||
What needs to change for .NET 10?
|
||||
assertions:
|
||||
- type: "output_matches"
|
||||
pattern: "(WebHostBuilder|obsolete|WebApplication)"
|
||||
- type: "output_matches"
|
||||
pattern: "(WithOpenApi|deprecated|OpenAPI)"
|
||||
- type: "output_matches"
|
||||
pattern: "(IActionContextAccessor|obsolete)"
|
||||
rubric:
|
||||
- "Identifies WebHostBuilder/IWebHost/WebHost as obsolete; recommends WebApplication.CreateBuilder"
|
||||
- "Notes WithOpenApi() is deprecated"
|
||||
- "Notes IncludeOpenAPIAnalyzers property is deprecated and should be removed"
|
||||
- "Notes Microsoft.Extensions.ApiDescription.Client is deprecated"
|
||||
- "Identifies IActionContextAccessor as obsolete"
|
||||
- "Warns that IPNetwork and ForwardedHeadersOptions.KnownNetworks are obsolete"
|
||||
- "Notes Razor runtime compilation is obsolete; suggests dotnet watch / hot reload"
|
||||
- "Mentions cookie auth no longer redirects API endpoints to login — now returns 401 directly"
|
||||
timeout: 240
|
||||
|
||||
# --- Scenario 3b: Microsoft.OpenApi v2.x breaking API changes in transformers ---
|
||||
- name: "ASP.NET Core app with OpenAPI transformers using Microsoft.OpenApi v1 APIs"
|
||||
prompt: |
|
||||
I'm migrating a .NET 9 ASP.NET Core web API to .NET 10. We have custom OpenAPI transformers that manipulate the document. After upgrading Microsoft.AspNetCore.OpenApi to 10.0, we get compilation errors. Here's our code:
|
||||
|
||||
```csharp
|
||||
using Microsoft.OpenApi.Any;
|
||||
using Microsoft.OpenApi.Models;
|
||||
|
||||
options.AddOperationTransformer((operation, context, ct) =>
|
||||
{
|
||||
var param = operation.Parameters.FirstOrDefault(p => p.Name == "api-version");
|
||||
if (param is not null)
|
||||
{
|
||||
param.Schema.Example = new OpenApiString("1.0");
|
||||
}
|
||||
|
||||
operation.Responses.TryAdd("401", new OpenApiResponse { Description = "Unauthorized" });
|
||||
|
||||
var oAuthScheme = new OpenApiSecurityScheme
|
||||
{
|
||||
Reference = new OpenApiReference { Type = ReferenceType.SecurityScheme, Id = "oauth2" }
|
||||
};
|
||||
operation.Security = new List<OpenApiSecurityRequirement>
|
||||
{
|
||||
new() { [oAuthScheme] = scopes }
|
||||
};
|
||||
|
||||
return Task.CompletedTask;
|
||||
});
|
||||
|
||||
options.AddSchemaTransformer((schema, context, ct) =>
|
||||
{
|
||||
if (schema.Properties is not null)
|
||||
{
|
||||
foreach (var property in schema.Properties)
|
||||
{
|
||||
if (schema.Required?.Contains(property.Key) != true)
|
||||
property.Value.Nullable = false;
|
||||
}
|
||||
}
|
||||
return Task.CompletedTask;
|
||||
});
|
||||
```
|
||||
|
||||
What changed and how do I fix each piece?
|
||||
assertions:
|
||||
- type: "output_matches"
|
||||
pattern: "(OpenApiString|OpenApiAny|JsonNode|System\\.Text\\.Json)"
|
||||
- type: "output_matches"
|
||||
pattern: "(OpenApiSecurityScheme|Reference|OpenApiSecuritySchemeReference)"
|
||||
- type: "output_matches"
|
||||
pattern: "(Nullable|nullable|removed|schema)"
|
||||
rubric:
|
||||
- "Identifies OpenApiString/OpenApiAny types are removed; recommends using System.Text.Json.Nodes.JsonNode (e.g., JsonNode.Parse)"
|
||||
- "Identifies OpenApiSecurityScheme.Reference pattern is replaced by OpenApiSecuritySchemeReference"
|
||||
- "Warns that operation.Parameters and other collections may now be null; must add null checks or initialize"
|
||||
- "Identifies OpenApiSchema.Nullable is removed in OpenAPI 3.1 / Microsoft.OpenApi v2; schema transformer can be removed or rewritten"
|
||||
- "Notes the namespace changes: Microsoft.OpenApi.Any removed, models moved to Microsoft.OpenApi"
|
||||
timeout: 240
|
||||
|
||||
# --- Scenario 4a: EF Core (Azure SQL JSON, parameterized collections, multi-target tools) ---
|
||||
- name: "EF Core app with Azure SQL JSON columns and parameterized collections"
|
||||
prompt: |
|
||||
I'm migrating a .NET 9 app using EF Core with UseAzureSql. The codebase:
|
||||
- Has primitive collections (string[] Tags) and owned types mapped to JSON columns (currently nvarchar(max))
|
||||
- Uses .Contains() on List<int> in LINQ queries that currently use OPENJSON
|
||||
- The project multi-targets net9.0 and net10.0, and we run `dotnet ef migrations add` without --framework
|
||||
What EF Core 10 breaking changes affect us?
|
||||
assertions:
|
||||
- type: "output_matches"
|
||||
pattern: "(json|nvarchar|data type)"
|
||||
- type: "output_matches"
|
||||
pattern: "(parameterized|multiple parameters|OPENJSON|Contains)"
|
||||
- type: "output_matches"
|
||||
pattern: "(--framework|multi-target)"
|
||||
rubric:
|
||||
- "Warns about json data type replacing nvarchar(max) on Azure SQL and how to mitigate with UseCompatibilityLevel(160)"
|
||||
- "Explains parameterized collections changed from OPENJSON to multiple scalar parameters; provides UseParameterizedCollectionMode mitigation"
|
||||
- "Notes EF tools require --framework for multi-targeted projects"
|
||||
timeout: 240
|
||||
|
||||
# --- Scenario 4b: EF Core (ExecuteUpdate, complex types, diagnostics logger) ---
|
||||
- name: "EF Core app with dynamic ExecuteUpdate and complex types"
|
||||
prompt: |
|
||||
I'm migrating a .NET 9 app using EF Core. Here's the code that builds dynamic ExecuteUpdateAsync calls:
|
||||
```csharp
|
||||
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: new[] { typeof(string) },
|
||||
arguments: new Expression[]
|
||||
{
|
||||
Expression.Lambda<Func<Blog, string>>(
|
||||
Expression.Property(blogParameter, nameof(Blog.Name)), blogParameter),
|
||||
Expression.Constant("foo")
|
||||
}),
|
||||
setters.Parameters);
|
||||
}
|
||||
await context.Blogs.ExecuteUpdateAsync(setters);
|
||||
```
|
||||
|
||||
We also have complex types:
|
||||
```csharp
|
||||
modelBuilder.Entity<Customer>(b => {
|
||||
b.ComplexProperty(c => c.ShippingAddress);
|
||||
b.ComplexProperty(c => c.BillingAddress);
|
||||
});
|
||||
// Both ShippingAddress and BillingAddress have a Street property
|
||||
// Nested: Order.Payment.BillingAddress.City
|
||||
```
|
||||
|
||||
And a custom diagnostics logger:
|
||||
```csharp
|
||||
public class MyLogger : IRelationalCommandDiagnosticsLogger
|
||||
{
|
||||
public InterceptionResult<DbDataReader> CommandReaderExecuting(
|
||||
IRelationalConnection connection, DbCommand command,
|
||||
DbContext context, Guid commandId, Guid connectionId,
|
||||
DateTimeOffset startTime)
|
||||
{ /* logging */ }
|
||||
}
|
||||
```
|
||||
What breaks in EF Core 10 and how do I fix each piece of code?
|
||||
assertions:
|
||||
- type: "output_matches"
|
||||
pattern: "(ExecuteUpdate|expression|lambda|Func)"
|
||||
- type: "output_matches"
|
||||
pattern: "(complex type|column name|uniquif|full path)"
|
||||
- type: "output_matches"
|
||||
pattern: "(logCommandText|diagnostics|logger|parameter)"
|
||||
rubric:
|
||||
- "Identifies that ExecuteUpdateAsync now takes a regular lambda/delegate instead of an expression tree; shows the simplified replacement using a regular lambda with if/else"
|
||||
- "Warns about complex type column name uniquification — Street columns will get a number suffix"
|
||||
- "Warns about nested complex type full-path column naming — BillingAddress_City becomes Payment_BillingAddress_City"
|
||||
- "Identifies the missing logCommandText string parameter in CommandReaderExecuting"
|
||||
timeout: 240
|
||||
|
||||
# --- Scenario 5: Microsoft.Data.Sqlite DateTimeOffset (all 3 high-impact changes) ---
|
||||
- name: "SQLite app with DateTimeOffset timezone handling"
|
||||
prompt: |
|
||||
I have a .NET 9 app using Microsoft.Data.Sqlite. It stores timestamps in TEXT columns without timezone offsets (e.g., '2024-06-15 14:30:00') and reads them back with GetDateTimeOffset(). It also writes DateTimeOffset values to REAL columns and reads timestamps with offsets using GetDateTime(). The app runs on machines in different timezones and we rely on the current local-timezone interpretation. What changes in .NET 10?
|
||||
assertions:
|
||||
- type: "output_matches"
|
||||
pattern: "(UTC|timezone|time zone|offset)"
|
||||
- type: "output_matches"
|
||||
pattern: "(GetDateTimeOffset|GetDateTime|DateTimeOffset)"
|
||||
rubric:
|
||||
- "Explains GetDateTimeOffset without an offset now assumes UTC instead of local timezone"
|
||||
- "Explains writing DateTimeOffset to REAL columns now converts to UTC first"
|
||||
- "Explains GetDateTime with an offset now returns UTC with DateTimeKind.Utc instead of Local"
|
||||
- "Mentions the AppContext switch Microsoft.Data.Sqlite.Pre10TimeZoneHandling as a temporary mitigation"
|
||||
- "Warns that all three changes are high-impact and recommends reviewing all date/time handling code"
|
||||
timeout: 180
|
||||
|
||||
# --- Scenario 6: Extensions obscure details (config null arrays, ProviderAlias assembly, keyed services AnyKey) ---
|
||||
- name: "Worker service with config null array binding and ProviderAlias assembly change"
|
||||
prompt: |
|
||||
I'm upgrading a .NET 9 worker service to .NET 10. I have specific concerns:
|
||||
- My appsettings.json has `"AllowedOrigins": [null, null]` and `"EmptyList": []`. In .NET 9, AllowedOrigins binds as `["", ""]` and EmptyList binds as null. What happens in .NET 10?
|
||||
- I have `"ConnectionString": null` in my JSON config and my class has `public string ConnectionString { get; set; } = "default";` — in .NET 9 this was bound as an empty string. Does this change?
|
||||
- My logging provider uses `[ProviderAlias("custom")]` and I reference it by assembly-qualified name `Microsoft.Extensions.Logging.ProviderAliasAttribute, Microsoft.Extensions.Logging`. Will that still resolve?
|
||||
- I register services with `services.AddKeyedSingleton<ICache, RedisCache>("redis")` and discover them with `provider.GetKeyedServices<ICache>(KeyedService.AnyKey)`. There was a bug in .NET 9 where this didn't work correctly. Is it fixed?
|
||||
- My BackgroundService does database seeding synchronously at the start of ExecuteAsync before the first await, and other services depend on the database being seeded. Will this still work?
|
||||
assertions:
|
||||
- type: "output_matches"
|
||||
pattern: "(null|empty|array|\\[\\]|binding)"
|
||||
- type: "output_matches"
|
||||
pattern: "(ProviderAlias|Abstractions|assembly)"
|
||||
- type: "output_matches"
|
||||
pattern: "(BackgroundService|ExecuteAsync|background thread|StartAsync)"
|
||||
rubric:
|
||||
- "Correctly states AllowedOrigins will bind as [null, null] instead of ['', ''] in .NET 10"
|
||||
- "Correctly states EmptyList will bind as an empty array [] instead of null"
|
||||
- "Correctly states ConnectionString will be overwritten with null, losing the 'default' constructor value"
|
||||
- "Notes ProviderAliasAttribute moved from Microsoft.Extensions.Logging to Microsoft.Extensions.Logging.Abstractions — assembly-qualified name will fail"
|
||||
- "Confirms GetKeyedServices with AnyKey is fixed in .NET 10"
|
||||
- "Warns BackgroundService.ExecuteAsync now runs on a background thread; database seeding will not block startup; recommends StartAsync or IHostedLifecycleService"
|
||||
timeout: 240
|
||||
|
||||
# --- Scenario 7: Cryptography (OpenSSL, certificate validation, ML-DSA, Rfc2898DeriveBytes) ---
|
||||
- name: "Cryptography app with OpenSSL, X.509, and Rfc2898DeriveBytes"
|
||||
prompt: |
|
||||
I'm migrating a .NET 9 Linux server app to .NET 10. It:
|
||||
- Uses OpenSSL-based crypto and runs on a system with OpenSSL 1.0.2
|
||||
- Also has a macOS build that uses SafeEvpPKeyHandle.OpenSslVersion to check the OpenSSL version
|
||||
- Validates X.509 certificates and parses X500DistinguishedName values that sometimes have unusual formatting
|
||||
- Uses new Rfc2898DeriveBytes(password, salt, iterations) to derive keys
|
||||
- Uses ML-DSA (post-quantum) and accesses the SecretKey property
|
||||
- Sets the DOTNET_SYSTEM_GLOBALIZATION_INVARIANT and old OpenSSL version override environment variables
|
||||
What do I need to change?
|
||||
assertions:
|
||||
- type: "output_matches"
|
||||
pattern: "(OpenSSL|1\\.1\\.1|openssl)"
|
||||
- type: "output_matches"
|
||||
pattern: "(macOS|unsupported)"
|
||||
- type: "output_matches"
|
||||
pattern: "(Rfc2898|SYSLIB0060|Pbkdf2)"
|
||||
- type: "output_matches"
|
||||
pattern: "(SecretKey|PrivateKey|renamed)"
|
||||
rubric:
|
||||
- "Warns that OpenSSL 1.1.1+ is required on Unix — OpenSSL 1.0.2 is no longer supported"
|
||||
- "Notes OpenSSL primitives are not supported on macOS"
|
||||
- "Identifies Rfc2898DeriveBytes constructors as obsolete (SYSLIB0060) and recommends Rfc2898DeriveBytes.Pbkdf2"
|
||||
- "Identifies MLDsa SecretKey renamed to PrivateKey"
|
||||
- "Mentions X500DistinguishedName validation is stricter"
|
||||
- "Mentions DOTNET_OPENSSL_VERSION_OVERRIDE environment variable rename"
|
||||
timeout: 180
|
||||
|
||||
# --- Scenario 8: SDK/NuGet obscure changes (NU1510, PrunePackageReference, deps.json, custom culture) ---
|
||||
- name: "SDK and NuGet obscure tooling changes"
|
||||
prompt: |
|
||||
I'm upgrading a .NET 9 project to .NET 10 and seeing unexpected behavior:
|
||||
- After `dotnet restore`, I get NU1510 warnings saying some of my direct PackageReferences have been "pruned". What does this mean and how do I fix it?
|
||||
- I have a PackageReference to a package that only provides analyzers (no runtime assets). In .NET 9 it appeared in deps.json but now it doesn't. Our deployment script reads deps.json to verify all expected packages are present.
|
||||
- My project uses PrunePackageReference — how does this interact with direct references in .NET 10?
|
||||
- My CI script runs `dotnet new sln --name MySolution` and then parses the resulting .sln file to extract project GUIDs. This broke.
|
||||
- My project has satellite resource assemblies for a custom culture (e.g., "x-custom"). The build behavior seems different.
|
||||
- I have a PackageReference without a Version attribute because we were relying on implicit resolution from a parent. Now restore fails.
|
||||
- Our CI sets NUGET_ENABLE_ENHANCED_HTTP_RETRY=true and uses HTTP (not HTTPS) NuGet sources with `dotnet package list`.
|
||||
What changed?
|
||||
assertions:
|
||||
- type: "output_matches"
|
||||
pattern: "(NU1510|pruned|prune)"
|
||||
- type: "output_matches"
|
||||
pattern: "(deps\\.json|runtime assets)"
|
||||
- type: "output_matches"
|
||||
pattern: "(SLNX|slnx|\\.slnx|sln.*format)"
|
||||
- type: "output_matches"
|
||||
pattern: "(PackageReference|[Vv]ersion|error)"
|
||||
rubric:
|
||||
- "Explains NU1510 means the package is provided by the shared framework and the direct reference was pruned; recommends removing the explicit PackageReference"
|
||||
- "Notes packages with no runtime assets are no longer in deps.json; deployment script must be updated"
|
||||
- "Explains PrunePackageReference now privatizes direct prunable references (PrivateAssets=All)"
|
||||
- "Warns dotnet new sln creates SLNX format; recommends --format sln for CI scripts"
|
||||
- "Notes MSBuild custom culture resource handling has changed"
|
||||
- "Notes PackageReference without a version now raises an error instead of implicitly resolving"
|
||||
- "Notes NUGET_ENABLE_ENHANCED_HTTP_RETRY env var was removed and HTTP sources now cause errors in dotnet package list"
|
||||
timeout: 240
|
||||
|
||||
# --- Scenario 9: Serialization (STJ property name conflict, XmlSerializer, LDAP) ---
|
||||
- name: "JSON polymorphism with conflicting property names and XmlSerializer"
|
||||
prompt: |
|
||||
I'm migrating a .NET 9 app to .NET 10. Here's code that worked before:
|
||||
|
||||
```csharp
|
||||
[JsonPolymorphic(TypeDiscriminatorPropertyName = "Type")]
|
||||
[JsonDerivedType(typeof(Dog), "dog")]
|
||||
public abstract class Animal
|
||||
{
|
||||
public abstract string Type { get; }
|
||||
}
|
||||
|
||||
public class Dog : Animal
|
||||
{
|
||||
public override string Type => "Dog";
|
||||
}
|
||||
|
||||
// This produced {"Type":"dog","Type":"Dog"} in .NET 9 — broken JSON but it serialized
|
||||
string json = JsonSerializer.Serialize<Animal>(new Dog());
|
||||
```
|
||||
|
||||
```csharp
|
||||
public class OrderDto
|
||||
{
|
||||
public int Id { get; set; }
|
||||
[Obsolete("Use ShippingAddress instead")]
|
||||
public string Address { get; set; }
|
||||
public string ShippingAddress { get; set; }
|
||||
}
|
||||
var xml = new XmlSerializer(typeof(OrderDto));
|
||||
// In .NET 9, Address was not included in the serialized output
|
||||
```
|
||||
|
||||
The app also validates email addresses like `user..name@example.com` with MailAddress, and uses Uri for 10K+ character URLs.
|
||||
What breaks?
|
||||
assertions:
|
||||
- type: "output_matches"
|
||||
pattern: "(property name|conflict|\\$type|discriminator|JsonIgnore|InvalidOperation)"
|
||||
- type: "output_matches"
|
||||
pattern: "(XmlSerializer|Obsolete|XmlIgnore)"
|
||||
- type: "output_matches"
|
||||
pattern: "(MailAddress|consecutive dots)"
|
||||
rubric:
|
||||
- "Identifies the Animal/Dog Type property now throws InvalidOperationException because it conflicts with the discriminator; recommends [JsonIgnore] or renaming"
|
||||
- "Notes the [Obsolete] Address property will now be included in XML serialization; add [XmlIgnore] to exclude it"
|
||||
- "Notes MailAddress now rejects consecutive dots"
|
||||
- "Mentions Uri length limits have been removed"
|
||||
timeout: 180
|
||||
|
||||
# --- Scenario 10: WinForms + WPF combined desktop app ---
|
||||
- name: "WinForms and WPF desktop app with System.Drawing and DynamicResource"
|
||||
prompt: |
|
||||
I'm upgrading a .NET 9 Windows desktop app to .NET 10. Here's code that needs migration:
|
||||
|
||||
```csharp
|
||||
// Form1.cs
|
||||
var menu = new ContextMenu();
|
||||
menu.MenuItems.Add(new MenuItem("Open", OnOpen));
|
||||
```
|
||||
|
||||
```csharp
|
||||
// ImageProcessor.cs
|
||||
try
|
||||
{
|
||||
using var bmp = new Bitmap(path);
|
||||
bmp.SetResolution(300, 300);
|
||||
}
|
||||
catch (OutOfMemoryException ex)
|
||||
{
|
||||
logger.LogError(ex, "Invalid image format");
|
||||
}
|
||||
```
|
||||
|
||||
```xml
|
||||
<!-- MainWindow.xaml -->
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions/>
|
||||
<Grid.RowDefinitions/>
|
||||
<TextBlock Text="{DynamicResource MissingKey}" />
|
||||
</Grid>
|
||||
```
|
||||
|
||||
The project has both `<UseWindowsForms>true</UseWindowsForms>` and `<UseWPF>true</UseWPF>`.
|
||||
The StatusStrip uses default rendering. TreeView has checkboxes.
|
||||
What do I need to fix in each code snippet?
|
||||
assertions:
|
||||
- type: "output_matches"
|
||||
pattern: "(MenuItem|ContextMenu|disambiguate|ambiguous|fully qualified)"
|
||||
- type: "output_matches"
|
||||
pattern: "(OutOfMemoryException|ExternalException)"
|
||||
- type: "output_matches"
|
||||
pattern: "(ColumnDefinitions|RowDefinitions|empty|disallowed|remove)"
|
||||
- type: "output_matches"
|
||||
pattern: "(DynamicResource|crash)"
|
||||
rubric:
|
||||
- "Identifies MenuItem/ContextMenu ambiguity and shows fix with fully qualified System.Windows.Forms.MenuItem"
|
||||
- "Identifies the catch block must change to catch ExternalException (not ArgumentException, not IOException — specifically ExternalException from System.Runtime.InteropServices)"
|
||||
- "Identifies the empty ColumnDefinitions/RowDefinitions must be removed from XAML"
|
||||
- "Warns the DynamicResource with MissingKey will now crash instead of being silently ignored"
|
||||
- "Mentions StatusStrip default RenderMode change"
|
||||
- "Mentions TreeView checkbox image truncation change"
|
||||
timeout: 180
|
||||
|
||||
# --- Scenario 11: Containers, single-file, and native interop ---
|
||||
- name: "Containerized single-file app with P/Invoke and IDispatchEx"
|
||||
prompt: |
|
||||
I'm migrating a .NET 9 app to .NET 10. Here's what we have:
|
||||
|
||||
```dockerfile
|
||||
FROM mcr.microsoft.com/dotnet/aspnet:9.0 AS base
|
||||
RUN apt-get update && apt-get install -y libgdiplus libc6-dev
|
||||
FROM mcr.microsoft.com/dotnet/sdk:9.0 AS build
|
||||
```
|
||||
|
||||
```csharp
|
||||
// NativeInterop.cs
|
||||
[DllImport("mylib")]
|
||||
[DefaultDllImportSearchPaths(DllImportSearchPath.System32)]
|
||||
static extern int ProcessData(byte* input, int length);
|
||||
```
|
||||
|
||||
```csharp
|
||||
// ComHelper.cs
|
||||
var dispatchEx = (IDispatchEx)comObject;
|
||||
var reflect = (IReflect)dispatchEx; // cast to IReflect
|
||||
```
|
||||
|
||||
```json
|
||||
// global.json
|
||||
{ "sdk": { "version": "9.0.200" } }
|
||||
```
|
||||
|
||||
The app is published with `dotnet publish -r linux-x64 --self-contained /p:PublishSingleFile=true` and also has a NativeAOT Linux build that relies on rpath.
|
||||
What needs to change for .NET 10?
|
||||
assertions:
|
||||
- type: "output_matches"
|
||||
pattern: "(Ubuntu|Debian|container|aspnet:10)"
|
||||
- type: "output_matches"
|
||||
pattern: "(DllImport|AssemblyDirectory|single-file|System32)"
|
||||
- type: "output_matches"
|
||||
pattern: "(IDispatchEx|IReflect|cast|COM)"
|
||||
rubric:
|
||||
- "Updates the Dockerfile base images to 10.0 and warns that default images are now Ubuntu, not Debian; apt-get commands may need adjustment"
|
||||
- "Identifies the P/Invoke will fail to find mylib in the app directory because DllImportSearchPath.System32 excludes it; recommends adding DllImportSearchPath.AssemblyDirectory"
|
||||
- "Warns the IDispatchEx to IReflect cast now fails in .NET 10"
|
||||
- "Mentions NativeAOT rpath is no longer set automatically"
|
||||
- "Notes global.json should be updated to 10.0.x SDK"
|
||||
timeout: 180
|
||||
|
||||
# --- Scenario 12: SslStream obsoletions + SYSLIB warnings ---
|
||||
- name: "App using SslStream properties and SystemEvents"
|
||||
prompt: |
|
||||
I'm upgrading a .NET 9 app to .NET 10. It:
|
||||
- Reads SslStream.KeyExchangeAlgorithm, SslStream.CipherAlgorithm, SslStream.HashAlgorithm, and their strength properties for TLS connection logging
|
||||
- Registers a callback on SystemEvents.EventsThreadShutdown for cleanup
|
||||
- Uses Queryable.MaxBy with an IComparer<TSource> overload
|
||||
- Has XSLT transforms with XsltSettings.EnableScript = true
|
||||
What warnings or errors will I get?
|
||||
assertions:
|
||||
- type: "output_matches"
|
||||
pattern: "(SYSLIB005[89]|NegotiatedCipherSuite|SslStream)"
|
||||
- type: "output_matches"
|
||||
pattern: "(SYSLIB006[012]|EventsThreadShutdown|MaxBy|EnableScript)"
|
||||
rubric:
|
||||
- "Identifies SYSLIB0058 for SslStream properties; recommends NegotiatedCipherSuite"
|
||||
- "Identifies SYSLIB0059 for SystemEvents.EventsThreadShutdown; recommends AppDomain.ProcessExit"
|
||||
- "Identifies SYSLIB0061 for Queryable.MaxBy/MinBy IComparer<TSource> overloads"
|
||||
- "Identifies SYSLIB0062 for XsltSettings.EnableScript"
|
||||
timeout: 180
|
||||
|
||||
# --- Scenario 13: NuGet restore + transitive auditing + InlineArray ---
|
||||
- name: "Library with NuGet auditing, transitive deps, and InlineArray"
|
||||
prompt: |
|
||||
I'm upgrading a .NET 9 class library to .NET 10. It:
|
||||
- Has an [InlineArray(16)] struct with an explicit [StructLayout(LayoutKind.Explicit, Size = 64)]
|
||||
- Uses DriveInfo.DriveFormat on Linux and compares the result to specific strings
|
||||
- Uses dotnet restore and we're seeing new NU1510 warnings about pruned references
|
||||
- The project has transitive NuGet dependencies that haven't been audited for vulnerabilities
|
||||
- Has FilePatternMatch usage that checks if Stem is null
|
||||
What should we fix?
|
||||
assertions:
|
||||
- type: "output_matches"
|
||||
pattern: "(InlineArray|StructLayout|explicit.*[Ss]ize|disallowed)"
|
||||
- type: "output_matches"
|
||||
pattern: "(NU1510|pruned|transitive|audit)"
|
||||
rubric:
|
||||
- "Warns that InlineArray structs with explicit Size in StructLayout are now disallowed (binary incompatible — won't load)"
|
||||
- "Explains DriveInfo.DriveFormat now returns actual Linux filesystem types instead of generic values"
|
||||
- "Explains NU1510 for pruned references and recommends removing the explicit PackageReference"
|
||||
- "Notes dotnet restore now audits transitive packages for vulnerabilities"
|
||||
- "Mentions FilePatternMatch.Stem is now non-nullable"
|
||||
timeout: 180
|
||||
|
||||
# --- Scenario 14: C# 14 compiler breaking changes (field keyword, extension keyword, scoped, partial, enumerator disposal) ---
|
||||
- name: "C# 14 compiler breaking changes — field keyword, extension keyword, disposal"
|
||||
prompt: |
|
||||
I'm migrating a .NET 9 library to .NET 10 / C# 14 and getting new compiler errors. Here's the code:
|
||||
|
||||
```csharp
|
||||
// FieldParser.cs
|
||||
public class FieldParser
|
||||
{
|
||||
private readonly JsonElement _payload;
|
||||
|
||||
public string ParsedValue
|
||||
{
|
||||
get
|
||||
{
|
||||
_payload.TryGetProperty("field", out var field);
|
||||
return field.GetString() ?? string.Empty;
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
```csharp
|
||||
// Extension.cs
|
||||
namespace MyLib.Plugins;
|
||||
public class extension
|
||||
{
|
||||
public string Name { get; set; }
|
||||
public void Load() { }
|
||||
}
|
||||
|
||||
// Used elsewhere:
|
||||
using alias = MyLib.Plugins.extension;
|
||||
```
|
||||
|
||||
```csharp
|
||||
// Iteration.cs
|
||||
var enumerator = GetItems().GetEnumerator();
|
||||
enumerator.MoveNext(); // True
|
||||
var first = enumerator.Current;
|
||||
enumerator.Dispose();
|
||||
enumerator.MoveNext(); // returned True in .NET 9, is this still the case?
|
||||
var second = enumerator.Current;
|
||||
```
|
||||
|
||||
```csharp
|
||||
// LambdaHelper.cs
|
||||
ref struct scoped { public int Value; }
|
||||
var fn = (scoped scoped s) => s.Value;
|
||||
```
|
||||
|
||||
What breaks in each case and how do I fix it?
|
||||
assertions:
|
||||
- type: "output_matches"
|
||||
pattern: "(field|CS9272|CS9258|keyword|property accessor)"
|
||||
- type: "output_matches"
|
||||
pattern: "(extension|keyword|contextual|rename|@extension)"
|
||||
- type: "output_matches"
|
||||
pattern: "(enumerator|[Dd]ispos|MoveNext|false)"
|
||||
rubric:
|
||||
- "Identifies 'var field' inside the property accessor as a CS9272 error because 'field' is now a keyword in property accessors; recommends renaming to fieldValue/@field"
|
||||
- "Identifies the class named 'extension' as breaking because extension is a contextual keyword in C# 14; recommends renaming or using @extension"
|
||||
- "Explains the enumerator now returns false from MoveNext() after disposal instead of continuing execution"
|
||||
- "Identifies the scoped type name conflict in the lambda — scoped is treated as a modifier; recommends @scoped for the type name"
|
||||
timeout: 180
|
||||
|
||||
# --- Scenario 15: HTTP streaming, GenericMath shifts, tar entries ---
|
||||
- name: "Blazor WASM app with generic math shift masking and tar operations"
|
||||
prompt: |
|
||||
I'm upgrading a .NET 9 Blazor WebAssembly app to .NET 10. Here's the code:
|
||||
|
||||
```csharp
|
||||
// GenericShift.cs
|
||||
static T ShiftLeft<T>(T value, int amount) where T : IShiftOperators<T, int, T>
|
||||
=> value << amount;
|
||||
|
||||
// Usage: ShiftLeft<byte>(0xFF, 8) returned 0 for byte but we got
|
||||
// inconsistent results with other small types in .NET 9
|
||||
```
|
||||
|
||||
```csharp
|
||||
// ApiClient.cs
|
||||
var response = await httpClient.GetAsync(url);
|
||||
var stream = await response.Content.ReadAsStreamAsync();
|
||||
var buffer = new byte[4096];
|
||||
int bytesRead = stream.Read(buffer, 0, buffer.Length); // synchronous read
|
||||
```
|
||||
|
||||
```csharp
|
||||
// Archiver.cs
|
||||
var entry = new PaxTarEntry(TarEntryType.RegularFile, "data.txt");
|
||||
// We expect entry.AccessTime and entry.ChangeTime to be set to now
|
||||
archive.WriteEntry(entry);
|
||||
```
|
||||
|
||||
```xml
|
||||
<!-- App.csproj -->
|
||||
<PublishTrimmed>true</PublishTrimmed>
|
||||
```
|
||||
|
||||
What breaks in each case and how do I fix it? Note: the app also uses HTTP/3 to connect to our backend.
|
||||
assertions:
|
||||
- type: "output_matches"
|
||||
pattern: "(shift|mask|generic math|IShiftOperators|byte|sizeof)"
|
||||
- type: "output_matches"
|
||||
pattern: "(streaming|synchronous|Read|browser|HttpClient|async)"
|
||||
- type: "output_matches"
|
||||
pattern: "(HTTP/3|trim|PublishTrimmed|Http3Support)"
|
||||
rubric:
|
||||
- "Explains that shift operations now consistently mask the shift amount; for byte, 8 & 7 = 0, so shifting by 8 always returns 0"
|
||||
- "Identifies the synchronous stream.Read() now throws because browser streaming is enabled by default; must use ReadAsync or disable streaming"
|
||||
- "Notes PaxTarEntry no longer auto-populates atime/ctime; set them explicitly"
|
||||
- "Notes HTTP/3 is disabled when PublishTrimmed is set; fix by adding <Http3Support>true</Http3Support> — this is NOT a native library issue, it's the trimmer removing HTTP/3 code"
|
||||
timeout: 240
|
||||
Reference in New Issue
Block a user