Skip to content

Upgrade to .NET 11 - #2665

Merged
ejsmith merged 2 commits into
mainfrom
feature/net11-rc2
Nov 11, 2026
Merged

ejsmith merged 2 commits into
mainfrom
feature/net11-rc2

Conversation

@ejsmith

@ejsmith ejsmith commented Oct 15, 2026 •

Copy link
Copy Markdown
Member

Upgrade the app to .NET 11 GA (SDK 11.0.100, runtime 11.0.0, released 2026-11-10) and refresh the .NET dependencies that upgrade cleanly. All projects target net11.0. global.json, CI, the Dockerfile, the dev container, VS Code launch paths, the CI shard runner, and the email-template preview script all point at .NET 11. Public routes, persisted data, and config keys are unchanged.

Also fixes two advisories published 2026-11-10 that fail restore on main too:

Advisory Fix
GHSA-f7vh-h5g3-7jrh Aspire elevation of privilege (high) Aspire → 13.6.5
GHSA-vmv3-wvjp-wqpj System.Formats.Cbor DoS (moderate), 9.0.5 via AWSSDK.Extensions.CborProtocol Pin System.Formats.Cbor 11.0.0 in Insulation

.NET 11.0.0 also includes the RC3 Brotli fix (CVE-2026-111497) that applies to the source-map HttpClient. main is still on 10.0.13; 10.0.15 has the fixes.

Runtime async is wired up as an opt-in switch (-p:UseRuntimeAsync=true) and is off. A/B benchmarks showed no measurable gain, and its known bugs are still open in GA.

Validation: Debug and Release builds with warnings as errors, the full backend suite (3,561 tests: 3,534 passed, 27 skipped, 0 failed; the skips are the opt-in assistant evals and two perf tests), and a clean dotnet list package --vulnerable --include-transitive.

Packages, runtime async, review notes, and verification

Package updates

Update Notes
Microsoft.Extensions.*, System.IO.Hashing, System.DirectoryServices, System.Security.Cryptography.Pkcs, Microsoft.AspNetCore.OpenApi, Microsoft.AspNetCore.Mvc.Testing, Microsoft.Extensions.TimeProvider.Testing → 11.0.0 Framework-aligned. The Pkcs pin stays because MimeKit still allows the vulnerable 10.0.0.
Microsoft.Extensions.Logging.Abstractions (removed) Now in the shared framework; NU1510 rejects the explicit reference.
Microsoft.OpenApi 2.12.2 → 3.11.0 (direct reference removed) Now comes transitively from Microsoft.AspNetCore.OpenApi 11 ([3.11.0, 4.0.0)). Two transformers now build the concrete OpenApiMediaType (upgrade guide).
Aspire 13.6.1 → 13.6.5 Security fix above; the MessagePack 3.1.12 override stays (13.6.5 still pulls 2.5.307).
OpenTelemetry 1.20.1 → 1.21.0, AspNetCore/Http instrumentation → 1.20.2 1.21.0 aligns with .NET 11. Its only breaking change is in the experimental OTLP disk retry, which we don't use.
ModelContextProtocol.AspNetCore 2.2.0 → 2.4.0 2.3.0, 2.4.0: OAuth and transport fixes. The breaking changes are client-side. MCP tests pass.
Scalar.AspNetCore 2.18.0 → 2.20.0, MailKit 4.18.1 → 4.18.2, Microsoft.NET.Test.Sdk 18.11.0 → 18.12.0, xunit.v3.mtp-v2 4.0.2 → 4.0.3, OTel StackExchangeRedis 1.20.0-beta.1 → 1.20.1-beta.1 Minor and patch releases.

Not updated:

  • MailKit 5.0.0: a major release with a porting guide; it drops BouncyCastle and moves to MimeKit.Core. It gets its own PR.
  • Stripe.net 53.1.0: changes the pinned Stripe API version, so it needs a billing-focused PR.

Runtime async (opt-in, off)

.NET 11 can compile async methods as runtime async methods: the runtime suspends and resumes them instead of compiler-generated state machines. In GA it is still a preview compiler feature, enabled with <Features>runtime-async=on</Features>. src/Directory.Build.props maps -p:UseRuntimeAsync=true to that.

The .NET libraries (Kestrel, MVC, routing, HttpClient) already ship compiled this way. Foundatio, the Elasticsearch client, and StackExchange.Redis use state machines regardless. The switch therefore only changes our own assemblies.

A/B results (RC3, Release, alternating processes against the test Elasticsearch, warmup excluded):

Scenario Off median On median Δ wall Δ CPU Δ alloc
Event pipeline, 1,008 events 3,636 ms 3,691 ms +1.5% (p=0.46) +6.4% (p=0.22) +3.4% (p=0.09)
POST /api/v2/events, 120 × 20 events 70.5 ms 64.2 ms −8.9% (p=0.15) −0.1% +0.5%
EventPostsJob drain, 2,400 events 938 ms 965 ms +2.9% (p=0.77) +1.9% +0.5%
GET /api/v2/events, 400 requests 611 ms 600 ms −1.7% (p=0.79) +3.3% +0.8%
GET /api/v2/stacks, 400 requests 337 ms 332 ms −1.5% (p=0.87) +2.3% 0.0%

Nothing is significant. Each request allocates 1 to 2 MB, mostly in Elasticsearch I/O and JSON, so removing our state machines doesn't register. These issues are still open in GA:

  • runtime#136000: a GC write-barrier bug. Its 11.0 backport (#136166) isn't in 11.0.0. Our code has none of the ref struct locals that trigger it.
  • runtime#133619: AsyncLocal values leak into catch … when filters. It is milestoned for .NET 12.
  • roslyn#83980: the opt-in syntax may change.

Review notes

  • Thread pool throughput: runtime#135855 reports 30–45% lower throughput than .NET 10 on Linux for multiplexed clients such as StackExchange.Redis. It is still open in GA and milestoned for .NET 12. Load-test the Redis-heavy paths before production.
  • OpenAPI: ASP.NET Core 11 defaults to OpenAPI 3.2. We pin 3.1 for swagger-typescript-api. The regenerated openapi.json is semantically unchanged apart from the version and the server URL's trailing slash.
  • Static web assets workaround: each Web SDK project now emits the same _framework/Microsoft.NET.Sdk.BlazorWeb.* hot-reload assets. The test project references both Web and Job, so its Debug build fails on the duplicates. It now sets StaticWebAssetsEnabled=false; the tests serve wwwroot from the Web content root. GA still needs this.
  • Exact double→decimal conversion (details): stored session durations are unchanged. CloseInactiveSessionsJobTests now compares at the stored double precision.
  • Host failures: IHost.RunAsync now throws when a BackgroundService fails. Program.Main then logs Fatal and exits with 1.
  • APM: ASP.NET Core adds OTel semantic-convention HTTP tags. 5xx requests gain error.type, and Activity.StatusDescription is no longer set.
  • Kestrel is stricter about conflicting Content-Length/Transfer-Encoding headers and HTTP/2 connection headers.
  • Containers: the sdk:11.0.100 and aspnet:11.0.0 images are Ubuntu 26.04, run as root, and listen on 8080. .NET 11 requires x86-64-v2.

Verification

dotnet build Exceptionless.slnx                      # Debug, exercises the static web assets workaround
dotnet build Exceptionless.slnx --configuration Release
dotnet test
dotnet list Exceptionless.slnx package --vulnerable --include-transitive

@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Oct 15, 2026 •

Copy link
Copy Markdown
Codex Review Summary: ✅ Completed

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-11-11T15:56:06.946602Z 75b01b5 New commits
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@ejsmith ejsmith changed the title Upgrade to .NET 11 RC2 Upgrade to .NET 11 RC3 Nov 2, 2026
Target net11.0 with SDK 11.0.100 and runtime 11.0.0 across projects, CI,
Docker images, and dev tooling, and move framework-aligned packages to
11.0.0. Microsoft.AspNetCore.OpenApi 11 requires Microsoft.OpenApi 3.x, so
adapt the transformers to the media type interfaces and keep the published
document on OpenAPI 3.1.

Fix two advisories published with the November servicing release: move
Aspire to 13.6.5 (GHSA-f7vh-h5g3-7jrh) and pin System.Formats.Cbor 11.0.0
over the 9.0.5 that AWSSDK.Extensions.CborProtocol pulls in
(GHSA-vmv3-wvjp-wqpj). Refresh OpenTelemetry, ModelContextProtocol,
Scalar, MailKit, and test packages.

Drop the Microsoft.Extensions.Logging.Abstractions reference now that it
ships in the shared framework, and disable static web assets in the test
project so the Web and Job hot reload assets don't collide.

Add an opt-in UseRuntimeAsync build property that compiles our async
methods with .NET 11 runtime async. It stays off by default: A/B runs of
the event pipeline and the event, stack, and ingestion endpoints showed no
measurable change in time, CPU, or allocations.
@ejsmith ejsmith changed the title Upgrade to .NET 11 RC3 Upgrade to .NET 11 Nov 11, 2026
Microsoft.AspNetCore.OpenApi 11.0.0 depends on Microsoft.OpenApi [3.11.0, 4.0.0),
so the explicit pin is redundant.
@github-actions

Copy link
Copy Markdown

Code Coverage

Package Line Rate Branch Rate Complexity Health
Exceptionless.AppHost 23% 22% 130 ❌
Exceptionless.Core 78% 70% 11007 ✔
Exceptionless.Insulation 51% 43% 370 ➖
Exceptionless.Web 86% 72% 9415 ✔
Summary 81% (28630 / 35508) 70% (14322 / 20508) 20922 ✔

@ejsmith
ejsmith merged commit 4de0fcd into main Nov 11, 2026
22 checks passed
@ejsmith
ejsmith deleted the feature/net11-rc2 branch November 11, 2026 21:59
This was referenced Nov 11, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants