.NET
.NET 11 Ships Next Month. Five Features Worth Your Attention.
.NET 11 goes GA on November 10, at .NET Conf. The release candidates have carried a go-live license since September, so what ships next month is, give or take some bug fixes, what you can install today.
There are seven previews and three release candidates of notes behind it, and most of it is the steady stream of JIT and library improvements every release brings. These are the five changes I think will actually change how you write and run .NET code, and one thing to know before you plan the upgrade.
1. Union types in C# 15
This is the one people have been asking for since C# 7 brought pattern matching. A union holds exactly one of a fixed set of types, and the compiler knows the full set:
public record Success(string Message);
public record Failure(int ErrorCode);
public union Result(Success, Failure);
static string Describe(Result result) => result switch
{
Success(var message) => message,
Failure(var errorCode) => $"Error {errorCode}"
};
There is no default arm, and none is needed: the switch is exhaustive. Add a third case to Result and every switch that doesn’t handle it becomes a compiler warning, instead of a runtime surprise in a code path nobody tested.
The case types don’t need to share a base class, so you can combine types you don’t own. When they do share one, C# 15 also adds closed classes: a class that can only be derived from inside its own assembly, which gives the compiler the same complete list of subtypes.
What makes this more than a language curiosity is the support around it. System.Text.Json serializes unions, ASP.NET Core describes them in OpenAPI and validates their cases, and analyzers can see the cases through Roslyn. One caveat: Swashbuckle and NSwag don’t recognise unions yet, so if you generate OpenAPI with them, check what they produce. Union types are part of C# 15, the default language version for net11.0, with no preview flag.
For the many codebases that model results with exceptions, nulls or a Status string, this is the cleanest way out so far.
2. Runtime async
Since 2012, async has been a compiler trick: every async method is rewritten into a state machine class. It works, but you pay for it in allocations and in stack traces full of AsyncMethodBuilderCore.Start.
In .NET 11 the runtime itself understands async methods, so it handles suspension and resumption. Microsoft’s three-method sample makes the difference plain: the live stack trace goes from 13 frames to 5, and the 5 are your actual methods.
The .NET runtime libraries are already compiled this way, so your app uses runtime async inside the framework without changing anything. Your own code, as of the release candidates, opts in with one property:
<PropertyGroup>
<Features>runtime-async=on</Features>
</PropertyGroup>
The performance work through the previews has been substantial. On the TechEmpower JSON benchmark, the allocation rate during warm-up dropped from about 110 MB/s to about 8 MB/s. Turn it on in a branch, run your load tests, and compare. Microsoft is explicitly asking for results, good or bad.
3. ASP.NET Core is secure and observable by default
Two changes here, both of the kind you would otherwise have to remember to add.
Cross-site request forgery protection is on by default. Apps built with WebApplication.CreateBuilder reject unsafe cross-origin browser requests, based on the Sec-Fetch-Site and Origin headers the browser already sends. It covers Minimal APIs, MVC, Razor Pages and Blazor, needs no tokens, and leaves same-origin requests and non-browser clients alone. If an endpoint legitimately accepts cross-origin form posts, opt it out explicitly, and check that before you upgrade, not after the support tickets.
OpenTelemetry tracing is built in. ASP.NET Core now puts the standard OpenTelemetry HTTP attributes (method, path, status code, server address) on the request activity itself. The separate instrumentation package is no longer needed:
builder.Services.AddOpenTelemetry()
.WithTracing(tracing => tracing
.AddSource("Microsoft.AspNetCore")
.AddOtlpExporter());
Defaults matter more than features. The secure, observable option is now the one you get by doing nothing.
4. File-based apps grow up
.NET 10 introduced dotnet run app.cs: a C# program in one file, no project. In .NET 11 it stops being a toy. A file-based app can pull in other files with #:include, reference another file-based app as a library with #:ref, and reference a compiled DLL:
#:include helpers.cs
#:include models/customer.cs
#:include ./libs/MyLibrary.dll
Console.WriteLine(Helpers.FormatOutput(new Customer()));
dotnet ef and dotnet user-jwts work with file-based apps too.
This is the release where C# becomes a reasonable choice for the scripts every team has lying around: the data fix, the migration helper, the release script that started in PowerShell and grew. They can now share code with your real projects and live next to them, in the same language and with the same types.
5. EF Core 11 closes old gaps
EF Core 11 is less one headline than a handful of things that used to send you to raw SQL:
FullJointranslates toFULL OUTER JOIN, completing theLeftJoinandRightJoinoperators added in .NET 10.- Vector search on SQL Server 2025.
VectorSearch()withWithApproximate()translates to SQL Server’s native approximate nearest-neighbour search, so retrieval for an AI feature can live in the database you already run. - JSON columns are first-class in the relational model, which fixes long-standing problems with partial updates and migrations.
- A new analyzer, EF1004, flags
ToAsyncEnumerable()on a query, which quietly runs it synchronously. UseAsAsyncEnumerable().
On SQL Server, also note that the default compatibility level is now SQL Server 2022. If your production database runs an older level, set it explicitly before EF starts generating SQL it can’t run.
The one thing to know before you upgrade
.NET 11 is a Standard Term Support release, supported until November 9, 2028. .NET 10, the current Long Term Support release, is supported until November 14, 2028.
They end five days apart.
The old advice was to stay on LTS unless you need something new, because STS releases expired first. That reason is gone. Moving from .NET 10 to .NET 11 now costs you no support time at all, and earns you everything above. The real question is whether your dependencies are ready, and for most mainstream libraries they will be within weeks of the launch.
If you’re still on .NET 8, its support ends in November 2026, the same month .NET 11 ships. That isn’t an upgrade you get to schedule anymore.
Found this useful? Share it.
A ready-made LinkedIn post is one click away, or share the link wherever your team talks.
.NET 11 ships on November 10. The release candidates have had a go-live license since September, so what ships is, give or take some bug fixes, what you can install today. Five changes I think will actually change how you write and run .NET: 1. Union types in C# 15. "public union Result(Success, Failure);" and the compiler knows every case. Switches are exhaustive, System.Text.Json serializes them, and ASP.NET Core describes them in OpenAPI. 2. Runtime async. The runtime, not a compiler-generated state machine, now runs async methods. Stack traces go from 13 frames to 5 in Microsoft's sample, and warm-up allocations dropped sharply in benchmarks. 3. ASP.NET Core is secure and observable by default. CSRF protection is on with no tokens, and OpenTelemetry tracing is built in without the extra instrumentation package. 4. File-based apps grow up. "dotnet run app.cs" can now include other files, reference other apps and DLLs. Your team's scripts can finally be C#. 5. EF Core 11 closes old gaps. FULL OUTER JOIN, vector search on SQL Server 2025, first-class JSON columns. And the one thing to know before you plan the upgrade: .NET 11 (STS) and .NET 10 (LTS) reach end of support five days apart, in November 2028. Staying on LTS no longer buys you any extra time. If you're still on .NET 8, its support ends this November too. {url}