This .NET 11 features guide is built for engineers who ship production EF Core 11 and ASP.NET Core 11 applications and need two straight answers: what's new, and what breaks when you upgrade. Every feature and breaking change below is verified against the official Microsoft Learn documentation ahead of the November 2026 General Availability release (.NET 11 Release Candidate 2 shipped on October 13, 2026 with a go-live license, though minor details can still shift before GA).
![]() |
| Overview of new features, SQL query optimizations, and breaking changes in EF Core 11 and ASP.NET Core 11 (.NET 11). |
Each section follows the same practical pattern: the old limitation in .NET 10 or EF Core 10, the fix in .NET 11, runnable net11.0 code, and the edge cases I'd check before deploying to production. Two items that frequently show up on release lists—generic type mappings and the structured JsonPath type—are strictly for database provider authors rather than app developers, so I've flagged them clearly so you don't waste time hunting for an API you'll never call. If you also want to catch up on the language itself, pair this guide with What's New in C# 15: Practical Features and Code Examples.
Summary Comparison: .NET 10 vs. .NET 11 (EF Core 11 & ASP.NET Core 11)
Here's the high-level map of new features, behavioral shifts, and breaking changes across EF Core 11 and ASP.NET Core 11:
| Area | Change | .NET 10 / EF Core 10 | .NET 11 / EF Core 11 | Type |
|---|---|---|---|---|
| EF Core | Complex types + JSON on TPT/TPC | Not supported on TPT/TPC hierarchies | Supported (UseTptMappingStrategy(), ToJson()) |
New |
| EF Core | Complex type configuration | ComplexProperty(...).Property(...) |
Property(e => e.Details.Description) |
New |
| EF Core | Cosmos DB complex types | Complex types not fully supported | Fully supported, embedded as nested JSON | New |
| EF Core | No-op SQL CAST |
Redundant casts could block index use | Stripped in SQL post-processing | Improvement |
| EF Core | SqlVector<T> columns |
Loaded automatically with the entity | Left out of SELECT; property is null |
Breaking (Low) |
| EF Core | Microsoft.Data.SqlClient |
6.x, Entra ID dependencies bundled | 7.0; Entra auth needs Microsoft.Data.SqlClient.Extensions.Azure |
Breaking (Medium) |
| EF Core | SQL Server compatibility level | Default 150 (SQL Server 2019) |
Default 160 (SQL Server 2022) |
Breaking (Low) |
| EF Core | Cosmos sync I/O | Throws unless you opt in | Always throws, no opt-in | Breaking (Medium) |
| EF Core | Cosmos unmapped JSON properties | Round-tripped through __jObject |
Ignored on read, lost on save | Breaking (High) |
| EF Core | NativeAOT groundwork | Experimental | Generic type mappings (providers), EFOptimizeContext removed |
Infrastructure |
| ASP.NET Core | Validation (Minimal APIs, Blazor) | Attributes experimental, no async or built-in localization | Attributes stable, async validators, built-in localization | New |
| ASP.NET Core | OpenAPI | Default 3.1 | Default 3.2, binary file responses, Microsoft.OpenApi 3.x |
New + Breaking |
| ASP.NET Core | OpenTelemetry HTTP tags | Needed OpenTelemetry.Instrumentation.AspNetCore |
Framework emits semantic-convention tags by default | Behavioral |
| ASP.NET Core | Blazor | Manual labels, button-based grid paging | DisplayName, QuickGrid URL paging & OnRowClick, SSR client validation |
New |
| ASP.NET Core | Static SSR antiforgery | Endpoint validated tokens itself | Middleware verdict, automatic CSRF protection | Behavioral |
| ASP.NET Core | ConcurrencyLimiter middleware |
Obsolete since .NET 8, still worked | Removed; use rate limiting | Breaking |
| ASP.NET Core | Kestrel protocol compliance | Rejected only Connection and TE on HTTP/2 & HTTP/3 |
Rejects additional connection-specific headers | Behavioral |
The 7 Upgrade Changes Most Likely to Bite First
- Cosmos DB drops unmapped JSON properties on save (High impact): Extra fields written by other services are no longer preserved via
__jObject. Microsoft.Data.SqlClient7.0 splits out Entra ID auth: Managed Identity and Azure AD connection strings fail at runtime unless you installMicrosoft.Data.SqlClient.Extensions.Azure.- SQL Server compatibility level defaults to
160: Older SQL Server 2019 (or unrestored compatibility level) databases can hit unsupported SQL syntax likeLEASTandGREATEST. SqlVector<T>properties come backnull: Vector columns are excluded from default entitySELECTqueries unless you project them explicitly.- OpenAPI documents default to v3.2: Downstream code generators, gateways, and custom
Microsoft.OpenApi3.x transformers may need updates. ConcurrencyLimitermiddleware is gone: Projects callingUseConcurrencyLimiter()won't compile onnet11.0; migrate toUseRateLimiter().- Hosting emits OpenTelemetry HTTP tags out of the box: Watch for duplicate span tags if you use an older version of
OpenTelemetry.Instrumentation.AspNetCore.
Part 1: What's New in EF Core 11
net11.0 with nullable reference types enabled.
1. EF Core 11 Complex Types with JSON, TPT, and TPC
The problem (.NET 10): Complex types and JSON columns couldn't be used on entities mapped with Table-per-Type (TPT) or Table-per-Concrete-Type (TPC) inheritance. If your Vehicle base class used TPT and you wanted a value object on it, you either flattened that value object into loose columns or abandoned TPT altogether.
The solution in EF Core 11: EF Core 11 supports complex types and JSON columns on both TPT and TPC hierarchies. Under TPT, the complex type's properties become columns on the base table (named like Registration_Plate). Mapping that same complex property to a single JSON column takes one extra call.
The Key Insight:
Your entity inheritance strategy (TPH, TPT, TPC) and the storage format of your value objects (flat columns vs. JSON) are now two independent architectural decisions.
using System.ComponentModel.DataAnnotations.Schema;
public abstract class Vehicle
{
public int Id { get; set; }
public required string Name { get; set; }
public required RegistrationInfo Registration { get; set; }
}
public class Car : Vehicle
{
public int Doors { get; set; }
}
public class Truck : Vehicle
{
public decimal PayloadTons { get; set; }
}
[ComplexType]
public class RegistrationInfo
{
public required string Plate { get; set; }
public DateOnly? FirstRegistered { get; set; }
}
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
// EF Core 11: TPT with the complex type as columns on the Vehicle table
// (Registration_Plate, Registration_FirstRegistered)
modelBuilder.Entity<Vehicle>().UseTptMappingStrategy();
// Or store the complex type as a single JSON column instead:
// modelBuilder.Entity<Vehicle>()
// .UseTptMappingStrategy()
// .ComplexProperty(v => v.Registration, b => b.ToJson());
}
When to use it & gotchas:
- Use complex types for domain concepts without their own identity—addresses, registrations, or money amounts. Complex types have value semantics and no key, which makes them fit JSON documents much better than owned entity types.
- EF Core 11 also fixes a batch of earlier complex-type bugs, including nested comparisons,
ExecuteUpdateassignments on nested properties, a TPT edge case withComplexProperty, and nullable complex properties stored as JSON in TPH. If you backed away from complex types in EF Core 9 or 10 because of those rough edges, retest now. - Under TPC, complex type columns repeat in every concrete table. That follows naturally from TPC's design, so always run
dotnet ef migrations addon a branch and read the generated SQL diff before touching an existing hierarchy.
2. Configure Complex Type Properties with Lambda Chaining (and Index Them)
The problem (.NET 10): Configuring a single scalar property inside a complex type meant hopping through ComplexProperty(...) first just to reach Property(...). Worse, keys and database indexes couldn't reach into complex types at all.
The solution in EF Core 11: Property() now accepts chained member access directly, and keys and indexes can target scalar properties nested inside non-collection complex types. On relational providers, indexes can also target paths inside complex types mapped to JSON columns.
// EF Core 10: hop through ComplexProperty first
modelBuilder.Entity<Customer>()
.ComplexProperty(c => c.Address)
.Property(a => a.PostalCode)
.HasMaxLength(12);
// EF Core 11: chain member access directly inside Property()
modelBuilder.Entity<Customer>()
.Property(c => c.Address.PostalCode)
.HasMaxLength(12);
// New in EF Core 11: index a field inside a complex type, by lambda or by name
modelBuilder.Entity<Customer>().HasIndex(c => c.Address.PostalCode);
modelBuilder.Entity<Customer>().HasIndex("Address.PostalCode");
// Relational + JSON: index a path inside a complex collection
modelBuilder.Entity<Order>().ComplexCollection(o => o.Items, b => b.ToJson());
modelBuilder.Entity<Order>().HasIndex("Items[].Sku");
// SQL Server JSON index (generates CREATE JSON INDEX)
modelBuilder.Entity<Customer>()
.ComplexProperty(c => c.Contact, b => b.ToJson().HasColumnType("json"));
modelBuilder.Entity<Customer>().HasIndex("Contact.Address.City");
Indexing Gotchas:
Complex collection index paths use [] for all elements or a numeric indexer for a single element. Native JSON indexes depend on your target SQL Server version supporting CREATE JSON INDEX, so test your migration against the real database engine version—not just a local container running a newer image.
3. Complex Types and Bulk Execution in Azure Cosmos DB
The problem (.NET 10): Embedded objects inside Cosmos DB documents had to be modeled as owned entity types. Because owned types still carry hidden entity identity, comparing two embedded objects inside the same document was awkward.
The solution in EF Core 11: Complex types are now fully supported in the Cosmos provider. They embed cleanly as nested JSON objects (or JSON arrays for ComplexCollection) across queries, inserts, and updates.
modelBuilder.Entity<Order>(b =>
{
b.ComplexProperty(o => o.ShippingAddress);
b.ComplexCollection(o => o.Lines);
});
// Opt in to parallel bulk execution (new in EF Core 11)
optionsBuilder.UseCosmos(
connectionString,
"OrdersDB",
o => o.BulkExecutionEnabled());
Several other major Cosmos DB capabilities ship in the same release:
| Cosmos DB Feature | What It Does in EF Core 11 |
|---|---|
| Transactional batches by default | AutoTransactionBehavior.Auto (default) groups operations per container and partition; Never restores old one-by-one writes; Always throws if operations can't fit in a single batch. |
| Bulk execution | BulkExecutionEnabled() executes document operations in parallel for high-volume ingestion. |
| Session token APIs | SessionTokenManagementMode, GetSessionToken(), and UseSessionToken() enable read-your-writes consistency across multi-instance web farms. |
| Indexing policy | HasAutomaticIndexing().Except(...), composite indexes, and index paths through complex types. |
System.Text.Json serializer |
Replaces Newtonsoft.Json inside the provider (this is also why unmapped properties are lost—see Part 2). |
4. Cleaner SQL in EF Core 11: No-Op CASTs, Join Pruning, and ORDER BY
The problem (.NET 10): EF Core occasionally emitted a redundant CAST to the exact type a column already stored—such as CAST([c].[Email] AS nvarchar(max)) on an nvarchar(max) column. It happened most often with value-converted properties, and wrapping an indexed column in CAST(...) can block index seeks.
The solution in EF Core 11: EF Core detects and strips these no-op casts during SQL post-processing without requiring any C# query changes.
var matches = await db.Customers
.Where(c => c.Email.StartsWith("ana"))
.ToListAsync();
-- Illustrative: EF Core 10 when the redundant cast was emitted
WHERE CAST([c].[Email] AS nvarchar(max)) LIKE N'ana%'
-- EF Core 11: clean index-friendly predicate
WHERE [c].[Email] LIKE N'ana%'
Two additional SQL optimizations land alongside cast stripping:
- With
AsSplitQuery(), EF no longer joins reference navigations (such as a parent lookup table) in the secondary SQL generated for collection queries. - EF no longer appends redundant reference-navigation keys to
ORDER BYclauses when the parent primary key already determines the sort order.
In the EF team's benchmarks, these cleanups measured 29% faster querying in a simple split-query scenario and 22% faster in a single-query scenario. Always compare execution plans on your hottest queries before and after upgrading, and expect SQL text diffs if your test suite asserts against raw generated SQL strings.
5. JSON Queries: JsonPathExists, JsonContains, and Structured JsonPath
The problem (.NET 10): Checking whether a JSON key/path existed meant dropping down to raw SQL, and membership checks (Contains) over JSON-stored primitive collections relied on a heavier OPENJSON translation.
The solution in EF Core 11:
EF.Functions.JsonPathExists()translates directly toJSON_PATH_EXISTSon SQL Server 2022 and later.- When you opt in to SQL Server 2025 via compatibility level
170,Containsover primitive collections stored as JSON translates toJSON_CONTAINS, which can leverage a native JSON index. EF.Functions.JsonContains()lets you invokeJSON_CONTAINSdirectly with a custom JSON path or search mode.
// SQL Server 2022+ (compatibility level 160+)
var rated = await db.Posts
.Where(p => EF.Functions.JsonPathExists(p.Metadata, "$.rating"))
.ToListAsync();
// SQL Server 2025: opt in to compatibility level 170
optionsBuilder.UseSqlServer(connectionString, o => o.UseCompatibilityLevel(170));
var tagged = await db.Posts
.Where(p => p.Tags.Contains("ef-core")) // Translates to JSON_CONTAINS at level 170
.ToListAsync();
var fiveStars = await db.Posts
.Where(p => EF.Functions.JsonContains(p.Metadata, 5, "$.rating") == 1)
.ToListAsync();
Null-Handling Nuance:
SQL Server's JSON_CONTAINS cannot search for null, so EF Core only emits it when it can prove at least one side is non-nullable. Otherwise, it safely falls back to the OPENJSON translation.
JsonPath on IColumnModification and ColumnModificationParameters changed from string? to a structured JsonPath struct with Segments, Ordinals, IsRoot, and AppendTo(StringBuilder). Unless you maintain an EF database provider or custom update SQL generator, you don't need to touch this API.
// Provider code in EF Core 10:
if (columnModification.JsonPath is null or "$") { /* update whole column */ }
// Provider code in EF Core 11:
if (columnModification.JsonPath is not { IsRoot: false }) { /* update whole column */ }
6. EF Core 11 NativeAOT: What Changed and What Is Still Experimental
The problem: NativeAOT requires everything EF Core traditionally discovers through runtime reflection and dynamic code generation to be resolved at compile time.
| EF Core 11 NativeAOT Change | Who It Affects |
|---|---|
Type mappings became generic (CoreTypeMapping<T>, RelationalTypeMapping<T>), allowing default value comparers to be constructed without reflection. Clone(...) drops its old clrType parameter. |
Database provider authors & custom type mapping authors |
The EFOptimizeContext MSBuild property was removed. Code generation is now controlled by EFScaffoldModelStage and EFPrecompileQueriesStage, and PublishAOT=true enables both automatically at publish. |
Apps using compiled models or precompiled queries |
Cosmos DB's default discriminator property in the EF model is now named Discriminator while the JSON payload still writes $type, because $type generated invalid C# identifiers in compiled models. |
Cosmos DB apps using compiled models |
// Provider or custom type-mapping authors:
// EF Core 10
public sealed class MoneyTypeMapping : RelationalTypeMapping { /* ... */ }
// EF Core 11: derive from the generic base; Clone(...) drops its clrType argument
public sealed class MoneyTypeMapping : RelationalTypeMapping<Money> { /* ... */ }
<!-- Replaces the removed EFOptimizeContext property -->
<PropertyGroup>
<EFScaffoldModelStage>publish</EFScaffoldModelStage>
<EFPrecompileQueriesStage>publish</EFPrecompileQueriesStage>
</PropertyGroup>
Production Reality Check:
Microsoft's EF NativeAOT and precompiled queries documentation still carries an Experimental warning recommending against production NativeAOT deployment. Until that guidance changes at GA, treat full EF NativeAOT as an experiment—though you can safely capture startup gains without NativeAOT using dotnet ef dbcontext optimize --precompile-queries.
7. Other EF Core 11 Features Worth a Look
| Feature | What It Gives You |
|---|---|
FullJoin (.NET 11 LINQ operator) |
Translates directly to SQL FULL JOIN, completing RRF/hybrid search queries without raw SQL. |
MaxByAsync / MinByAsync |
Fetches the entity with the maximum or minimum key, translated cleanly to TOP(1) + ORDER BY. |
GroupBy improvements |
Supports joins over per-group top results and aggregates over reference navigations via joins. |
HasVectorIndex(), VectorSearch(), WithApproximate() |
Approximate nearest-neighbor (ANN) vector search on SQL Server 2025 (experimental in SQL Server; APIs may evolve). |
| Full-text search migrations & ranking | HasFullTextCatalog(), HasFullTextIndex(), FreeTextTable(), and ContainsTable() with ranking scores. |
| Temporal period CLR properties | HasPeriodStart(e => e.PeriodStart) maps temporal columns directly to CLR properties without EF.Property. |
| Date/time translations | Expanded DateTimeOffset SQL translations plus EF.Functions.DateTrunc(). |
ExcludeForeignKeyFromMigrations() |
Keeps navigation relationships in the C# model while skipping physical FK constraint creation in SQL migrations. |
| Latest migration ID in snapshot | Embeds the latest migration ID inside the model snapshot so divergent Git branches trigger an obvious merge conflict. |
dotnet ef database update --add |
Scaffolds, compiles in-memory with Roslyn, and applies a migration in a single CLI command. |
| EF CLI quality-of-life flags | migrations remove --offline, --connection, .config/dotnet-ef.json, --context "*", and -NoBuild. |
| SQLite updates | Ordered group_concat (requires SQLite 3.44+) and UInt128 parameter binding. |
// Approximate vector search with a vector index (SQL Server 2025)
modelBuilder.Entity<Document>().HasVectorIndex(d => d.Embedding, "cosine");
var hits = await db.Documents
.VectorSearch(d => d.Embedding, queryEmbedding, "cosine")
.OrderBy(r => r.Distance)
.Take(5)
.WithApproximate()
.ToListAsync();
Part 2: EF Core 11 Breaking Changes
Moving from EF Core 10 to EF Core 11 breaking changes involves high, medium, and low-impact behavioral shifts. Review the complete matrix first, then inspect the code fixes below:
| EF Core 11 Breaking Change | Impact | Quick Fix |
|---|---|---|
| Cosmos: unmapped properties no longer preserved | High | Map all needed properties in EF, or use CosmosClient |
| Cosmos: synchronous I/O completely removed | Medium | Switch all queries, saves, and test setup to async APIs |
Microsoft.Data.SqlClient upgraded to 7.0 |
Medium | Add Microsoft.Data.SqlClient.Extensions.Azure for Entra auth |
Cosmos: projection evaluating to undefined throws |
Medium | Guard with EF.Functions.IsDefined or CoalesceUndefined |
Cosmos: illegal id characters no longer escaped |
Medium | Use the AppContext compatibility switch for legacy data |
Microsoft.Data.Sqlite 11 drops .NET Framework, UWP & unmaintained bundles |
Medium | Remain on 10.x for legacy targets or migrate to net11.0 |
Cosmos: __jObject shadow property removed |
Low | Use CosmosClient when raw document JSON access is required |
SQL Server compatibility level defaults to 160 |
Low | Call UseCompatibilityLevel(150) for SQL Server 2019 or older |
Migrate() throws when zero migrations exist |
Low | Remove the empty Migrate() call or suppress MigrationsNotFound |
EFOptimizeContext MSBuild property removed |
Low | Use EFScaffoldModelStage and EFPrecompileQueriesStage |
Tools packages drop transitive Design dependency |
Low | Reference Microsoft.EntityFrameworkCore.Design directly |
SqlVector<T> excluded from default SELECT |
Low | Project the vector property explicitly with .Select(...) |
Cosmos: empty owned collections materialize as empty, not null |
Low | Update code expecting null on empty collections |
Cosmos: model discriminator property named Discriminator |
Low | Update EF.Property<string>(e, "Discriminator") calls |
| Cosmos: floating-point values truncated to integer, not rounded | Low | Call Math.Round explicitly when rounding is required |
| Owned JSON collections without a key emit obsolete warning | Low | Migrate to ComplexCollection(..., b => b.ToJson()) |
Property() no longer configures primitive collections |
Low | Use PrimitiveCollection() instead |
| Split queries throw on concurrent row modification | Low | Add retry logic, use snapshot/serializable isolation, or AsSingleQuery() |
SqlVector<T> Columns Are Excluded from Default Entity Materialization
The problem (EF Core 10): Vector properties were loaded on every entity query just like strings or ints. Because embeddings carry hundreds or thousands of float values (e.g., 1,536 dimensions), routine entity queries dragged massive payloads across the network even when you never read the vector in C#.
What changed: SqlVector<T> properties are omitted from SELECT statements during standard entity materialization, leaving the property null on the returned object. They still work normally inside WHERE and ORDER BY clauses (including VectorDistance() and VectorSearch()). In the EF team's benchmark, skipping default vector loading ran nearly 9x faster against a local database and 22x faster against remote Azure SQL.
// Entity property (SQL Server 2025+):
[Column(TypeName = "vector(1536)")]
public SqlVector<float> Embedding { get; set; }
var docs = await db.Documents.OrderBy(d => d.Title).ToListAsync();
// EF Core 10: docs[0].Embedding contained the 1536-float vector
// EF Core 11: docs[0].Embedding is null (column omitted from SELECT)
// Similarity search still works: the vector is used in SQL ORDER BY, not returned
var closest = await db.Documents
.OrderBy(d => EF.Functions.VectorDistance("cosine", d.Embedding, queryVector))
.Take(5)
.ToListAsync();
// Need the vector values in C#? Project the column explicitly:
var vectors = await db.Documents
.Select(d => new { d.Id, d.Embedding })
.ToListAsync();
Silent Null Trap:
SqlVector<T> is a value/wrapper type often declared without ?, so the C# compiler won't warn you! Grep your codebase for reads of .Embedding (re-ranking pipelines, exports, vector store syncs) and switch them to explicit projections. Microsoft notes an opt-in API for automatic vector loading is planned before final GA—recheck release notes at launch.
Microsoft.Data.SqlClient 7.0 Splits Out Entra ID Authentication
The problem (EF Core 10): EF Core 10 depended on Microsoft.Data.SqlClient 6.x, which pulled heavy Azure and Entra ID packages (Azure.Core, Azure.Identity, Microsoft.Identity.Client) into every SQL Server project—even apps using plain SQL authentication.
What changed: EF Core 11 moves to SqlClient 7.0, which strips those transitive packages out. If your connection string uses ActiveDirectoryDefault, ActiveDirectoryInteractive, ActiveDirectoryManagedIdentity, or ActiveDirectoryServicePrincipal, you must add the new extension package explicitly (and note that ActiveDirectoryPassword is now obsolete):
<PackageReference Include="Microsoft.Data.SqlClient.Extensions.Azure" Version="7.0.0" />
Local development against a Docker SQL container with password auth won't catch this missing package—test Managed Identity authentication in your staging environment before production cutover.
SQL Server Compatibility Level Defaults to 160
The problem (EF Core 10): UseSqlServer defaulted to compatibility level 150 (SQL Server 2019).
What changed: The default bumps to 160 (SQL Server 2022). Queries using Math.Min, Math.Max, Least, Greatest, and certain Take/Skip patterns now emit T-SQL LEAST(...) and GREATEST(...). If your production database runs on SQL Server 2019 or older—or was restored from an older backup without raising its compatibility level—those queries will fail at runtime.
// Stopgap for databases still below compatibility level 160:
optionsBuilder.UseSqlServer(
connectionString,
o => o.UseCompatibilityLevel(150));
Run SELECT name, compatibility_level FROM sys.databases against production before deploying, because a database migrated onto SQL Server 2022 retains its legacy compatibility level until explicitly altered.
Cosmos DB Synchronous I/O Removal and Unmapped JSON Data Loss
1. Sync I/O always throws: Synchronous Cosmos calls have been discouraged since EF Core 9, where they blocked on async I/O under an opt-in switch and risked thread-pool deadlocks. In EF Core 11, synchronous I/O throws unconditionally with zero opt-in—don't forget test fixtures calling synchronous db.Database.EnsureCreated().
// Always throws in EF Core 11 (no opt-in switch exists):
var open = db.Orders.Where(o => o.Status == "Open").ToList();
db.SaveChanges();
// Required in EF Core 11:
var openAsync = await db.Orders.Where(o => o.Status == "Open").ToListAsync();
await db.SaveChangesAsync();
__jObject property and written back intact on SaveChangesAsync(). Because EF Core 11 switched the Cosmos provider from Newtonsoft.Json to System.Text.Json, unmapped JSON properties are ignored on read and permanently lost if EF saves that entity back. If another microservice writes fields your EF model doesn't map, either map every field in your C# entity or use CosmosClient directly for shared documents.
Easy-to-Miss EF Core 11 Breaking Changes
Migrate()with zero migrations throws: Callingdb.Database.Migrate()on a project with no migrations now throws instead of silently succeeding. Suppress withConfigureWarnings(w => w.Ignore(RelationalEventId.MigrationsNotFound))if intentional.- Split queries can throw
DbQueryConcurrencyException: Previously, concurrent modifications between split queries could silently return empty child collections. Now EF detects mismatches and throws—handle with an execution strategy retry, snapshot/serializable isolation, orAsSingleQuery(). Property()rejects primitive collections: ConfigureList<int>orstring[]columns viaPrimitiveCollection()instead ofProperty().- Owned JSON collections without keys warn: Replace keyless
OwnsMany(..., b => b.ToJson())mappings withComplexCollection(b => b.Posts, b => b.ToJson()). - Tools packages drop transitive
Designreference: AddMicrosoft.EntityFrameworkCore.Designdirectly withPrivateAssets="all"if your build scripts relied on it transitively. - Cosmos composite keys stop escaping
/ \ ? #: Documents written under legacy character escaping won't be found without turning on the legacyAppContextswitch (though note the old escaping could cause key collisions). - Cosmos optional projections evaluating to
undefinedthrow: Wrap optional projection paths withEF.Functions.IsDefined(...)orEF.Functions.CoalesceUndefined(...).
Part 3: ASP.NET Core 11 New Features
1. Minimal API & Blazor Validation (Microsoft.Extensions.Validation)
The problem (.NET 10): [ValidatableType] and [SkipValidation] shipped as experimental in .NET 10, forcing class libraries to suppress compiler warning ASP0029. Asynchronous database checks and localized validation messages required custom plumbing outside the pipeline, and nullable value types declared directly as Minimal API parameters weren't validated.
The solution in .NET 11:
ValidatableTypeAttributeandSkipValidationAttributeare now stable—delete yourASP0029suppressions.- Minimal API validation supports end-to-end async rules via
AsyncValidationAttributeandIAsyncValidatableObject, running property and collection-element checks concurrently. - Calling
AddLocalization()alongsideAddValidation()enables localized error messages and display names automatically, and customIStringLocalizerFactoryimplementations (backed by SQL or JSON) override default.resxfiles cleanly. - Blazor forms gain async validation support, and static SSR forms with
<DataAnnotationsValidator />get client-side validation out of the box.
Unified Validation Pipeline:
A single AddValidation() registration now gives you the exact same rules, resource keys, and async validators across both Minimal APIs and Blazor forms.
builder.Services.AddLocalization(); // Registers IStringLocalizerFactory
builder.Services.AddValidation(); // .NET 11 wires up localization automatically
builder.Services.AddScoped<IUserDirectory, SqlUserDirectory>();
var app = builder.Build();
app.MapPost("/signup", (SignupRequest request) =>
TypedResults.Created($"/users/{request.Username}", request));
app.Run();
public sealed class SignupRequest
{
[Display(Name = "UsernameLabel")] // Resource key for display name
[Required(ErrorMessage = "UsernameRequired")] // Resource key for error message
[UniqueUsername]
public string? Username { get; set; }
[Required, EmailAddress] // Default messages resolve by convention!
public string? Email { get; set; }
[SkipValidation]
public string? ReferralCode { get; set; }
}
public sealed class UniqueUsernameAttribute : AsyncValidationAttribute
{
// Required by the base class; Minimal APIs always execute IsValidAsync.
protected override ValidationResult? IsValid(object? value, ValidationContext context) =>
throw new InvalidOperationException("Use the async validation path.");
protected override async Task<ValidationResult?> IsValidAsync(
object? value,
ValidationContext context,
CancellationToken cancellationToken)
{
if (value is not string name)
return ValidationResult.Success;
var users = context.GetRequiredService<IUserDirectory>();
return await users.ExistsAsync(name, cancellationToken)
? new ValidationResult("That username is already taken.")
: ValidationResult.Success;
}
}
Convention-based localization & IValidationMessageFormatter: When an attribute omits an explicit ErrorMessage, ASP.NET Core 11 probes three resource keys in order before falling back to the built-in English string: {DeclaringType}_{MemberName}_{AttributeType}_Error, then {DeclaringType}_{AttributeType}_Error, and finally {AttributeType}_Error (meaning translating RequiredAttribute_Error once localizes every [Required] field in your app). Custom attributes with constructor parameters can implement IValidationMessageFormatter:
public sealed class MultipleOfAttribute(int factor)
: ValidationAttribute, IValidationMessageFormatter
{
public override bool IsValid(object? value) =>
value is not int n || n % factor == 0;
// {0} = localized display name, {1} = factor
public string FormatMessage(CultureInfo culture, string template, string displayName) =>
string.Format(culture, template, displayName, factor);
}
// Resolve all keys from one shared resource class:
builder.Services.AddValidation(options =>
{
options.LocalizerProvider = (_, factory) => factory.Create(typeof(ValidationMessages));
});
// Or back validation messages with a database or JSON localizer:
builder.Services.AddSingleton<IStringLocalizerFactory, DbStringLocalizerFactory>();
builder.Services.AddValidation();
When to use it & gotchas:
- Not for MVC controllers or Razor Pages: Those stacks continue using classic MVC model validation.
- Cross-assembly source generator trap: The validation source generator only scans types in the assembly where
AddValidation()is invoked! If your Minimal API endpoints live in a separate project (e.g.,MyApp.Endpoints.dll), callingAddValidation()only in the host project means invalid payloads will bypass validation and return200 OK. Expose an extension method inside the endpoints assembly that callsservices.AddValidation(), and call that fromProgram.cs. - Database race conditions: Async validation adds a round-trip per request and is only a user-friendly check—always keep a database
UNIQUEindex as your real concurrency guarantee.
2. OpenAPI 3.2 by Default and Binary File Responses
The problem (.NET 10): OpenApiVersion defaulted to 3.1, declaring multiple Produces metadata entries for the same HTTP status code silently dropped all but one, and binary file downloads lacked built-in schema generation.
The solution in .NET 11:
OpenApiOptions.OpenApiVersionnow defaults toOpenApiSpecVersion.OpenApi3_2(landed in .NET 11 Preview 6).FileContentResult(along withFileStreamResult,FileContentHttpResult, andFileStreamHttpResult) generates an OpenAPI schema withtype: stringandformat: binary.- All declared response types for a single status code are preserved—multiple types sharing one content type combine into an
anyOfschema. Microsoft.AspNetCore.OpenApiupgrades toMicrosoft.OpenApi3.x under the hood.
using Microsoft.AspNetCore.Mvc;
using Microsoft.OpenApi;
// .NET 11 defaults to OpenApi3_2; pin 3.1 explicitly if a downstream tool lags behind:
builder.Services.AddOpenApi(options =>
{
options.OpenApiVersion = OpenApiSpecVersion.OpenApi3_1;
});
app.MapOpenApi();
// Binary file response schema generation
app.MapGet("/invoices/{id:int}/pdf", (int id) =>
{
byte[] pdf = InvoiceRenderer.Render(id);
return TypedResults.File(pdf, "application/pdf");
})
.Produces<FileContentResult>(contentType: "application/pdf");
// Multiple content types on status 200: both are preserved in .NET 11!
app.MapGet("/items/{id:int}", (int id) => ItemStore.Find(id))
.Produces<Item>(200, "application/json")
.Produces<string>(200, "text/plain");
# Generated schema for the PDF endpoint:
responses:
'200':
content:
application/pdf:
schema:
$ref: '#/components/schemas/FileContentResult'
components:
schemas:
FileContentResult:
type: string
format: binary
| Additional OpenAPI Change | Detail in ASP.NET Core 11 |
|---|---|
HTTP QUERY method |
Recognized as an operation: emitted under query in OpenAPI 3.2, and under x-oai-additionalOperations in 3.0/3.1. |
| Server-Sent Events (SSE) | Endpoints returning SseItem<T> via TypedResults.ServerSentEvents emit the OpenAPI 3.2 itemSchema. |
[Obsolete] mapping |
Marks operations, schemas, and properties with deprecated: true automatically. |
| Build-time environment | New OpenApiGenerationEnvironment MSBuild property selects the host environment during build-time spec generation. |
Transformer & Tooling Gotchas:
Test your full OpenAPI pipeline (Swagger UI, Scalar, Kiota/NSwag client generators, API gateways) before shipping 3.2 in production. Note that one older section of Microsoft's What's New page still claims 3.2 is opt-in, whereas the newer official Breaking Change notice confirms OpenApi3_2 is the default. If you write custom schema/document transformers, Microsoft.OpenApi 3.x replaces OpenApiAny types with JsonNode and converts concrete types into interfaces like IOpenApiSchema.
3. Built-In OpenTelemetry HTTP Tags in ASP.NET Core 11
The problem (.NET 10): Microsoft.AspNetCore.Hosting.SuppressActivityOpenTelemetryData defaulted to true, meaning built-in HTTP server Activity spans carried zero OpenTelemetry semantic-convention tags unless you installed OpenTelemetry.Instrumentation.AspNetCore.
The solution in .NET 11: That switch now defaults to false. Out of the box, ASP.NET Core populates standard HTTP server-span attributes: http.request.method, http.response.status_code, http.route, network.protocol.version, url.path, url.scheme, server.address, server.port, and user_agent.original.
// .NET 10: required the external instrumentation package for HTTP tags
builder.Services.AddOpenTelemetry()
.WithTracing(t => t
.AddAspNetCoreInstrumentation()
.AddConsoleExporter());
// .NET 11: subscribe directly to the framework's built-in ActivitySource
builder.Services.AddOpenTelemetry()
.WithTracing(t => t
.AddSource("Microsoft.AspNetCore")
.AddConsoleExporter());
// Optional: restore pre-11 behavior early in Program.cs startup
AppContext.SetSwitch("Microsoft.AspNetCore.Hosting.SuppressActivityOpenTelemetryData", true);
Observability gotchas to check before rollout:
- Duplicate span tags: If both the framework and an older version of
OpenTelemetry.Instrumentation.AspNetCoreattach attributes, upgrade the instrumentation package—its latest build detects the framework's tags and skips duplicates. http.routeformatting on conventional routes: Tokens like{controller},{action},{area}, and{page}are now replaced by resolved values (e.g.,Store/Checkout/{id?}instead of the raw route template). Update Grafana/Datadog queries that group on old route strings.- Metric cardinality & status changes:
http.server.request.durationadds anerror.typedimension on 5xx responses (increasing time-series cardinality), andActivity.StatusDescriptionis no longer populated on unhandled exceptions (exceptions are still recorded as span events). Note that theAppContextswitch only suppresses the tags—it doesn't revert thehttp.routeorStatusDescriptionchanges.
4. Blazor in .NET 11: DisplayName, QuickGrid URL Paging, and Automatic CSRF
1. New DisplayName Component: Instead of hardcoding label text in every Razor form, <DisplayName For="..." /> reads metadata from [Display] (preferred, with full resource localization support) or [DisplayName], falling back to the C# property name.
<EditForm Model="Model" OnValidSubmit="Save">
<DataAnnotationsValidator />
<label>
<DisplayName For="@(() => Model.ProductionDate)" />
<InputDate @bind-Value="Model.ProductionDate" />
</label>
</EditForm>
@code {
private OrderForm Model { get; set; } = new();
public class OrderForm
{
[Required, Display(Name = "Production date")]
public DateTime ProductionDate { get; set; }
}
}
2. QuickGrid URL-Based Paging, Sorting, and OnRowClick: Previously, QuickGrid held pagination and sort state in memory and required an interactive render mode—breaking shareable URLs, browser Back/Forward buttons, and static SSR. In .NET 11, QuickGrid persists state in the query string (?page=2&sort=Name&direction=asc) and adds a clean OnRowClick callback.
<QuickGrid Items="@orders"
Pagination="@ordersPaging"
OnRowClick="@((Order o) => Open(o.Id))">
<PropertyColumn Property="@(o => o.Number)" Sortable="true" />
<PropertyColumn Property="@(o => o.Total)" />
</QuickGrid>
<Paginator State="@ordersPaging" />
@* A second grid on the same page needs distinct query parameter prefixes: *@
<QuickGrid Items="@returns"
Pagination="@returnsPaging"
QueryParameterNameOptions="@(new QueryParameterNameOptions("returns_"))">
...
</QuickGrid>
- QuickGrid gotchas: Sort query strings reference the column
Title(renaming a title silently resets bookmarked links to default sort). Sortable headers and paginator controls now render as<a>links rather than<button>elements, so update custom CSS selectors likebutton.col-title. If needed, revert viaMicrosoft.AspNetCore.Components.QuickGrid.EnableUrlBasedQuickGridNavigationAndSorting = false.
3. Static SSR Antiforgery & Automatic Header-Based CSRF Protection: In .NET 10, Razor Components endpoints validated antiforgery tokens themselves and always emitted form tokens even without app.UseAntiforgery(). In .NET 11, endpoints defer to the middleware verdict and only generate form tokens when token middleware runs. Meanwhile, WebApplication.CreateBuilder registers automatic CSRF protection inspecting Sec-Fetch-Site and Origin headers across Blazor SSR, form-binding Minimal APIs, and antiforgery-enabled MVC actions.
var app = builder.Build();
// Keep this on existing apps if you rely on hidden antiforgery form tokens!
// Removing it stops token generation and relies on Sec-Fetch-Site / Origin checks.
app.UseAntiforgery();
Safe Upgrade Rule:
Upgrade to .NET 11 with app.UseAntiforgery() still in place, then evaluate removing it in a separate, dedicated PR. Keep token middleware if you support legacy browsers without Sec-Fetch-Site, use IAntiforgeryAdditionalDataProvider, or have compliance rules requiring form tokens. If legitimate cross-origin form posts hit a 400, configure CORS rather than turning CSRF protection off.
5. Other ASP.NET Core 11 Features Worth Knowing
| Area | .NET 11 Addition | Why You'd Care |
|---|---|---|
| Minimal APIs | Endpoint filters run even when parameter binding fails (set RouteHandlerOptions.ThrowOnBadRequest = false in Development) |
Enforce a uniform JSON error envelope on 400 Bad Request responses |
| Minimal APIs & MVC | [ShortCircuit] attribute executes endpoints immediately after routing, bypassing downstream middleware |
Zero-overhead health checks, robots.txt, and metrics probes |
| SignalR | Live authentication token refresh without dropping connections + client cancellation of non-streaming hub calls | Long-lived WebSocket connections with short-lived JWTs |
| ASP.NET Core Identity | TimeProvider support, passkey display names inferred from authenticators, and dotnet user-jwts for file-based apps |
Deterministic auth unit tests and cleaner passwordless UX |
| Output caching | IOutputCachePolicyProvider extensibility |
Multi-tenant or database-driven cache policies |
| Blazor state & caching | [SupplyParameterFromTempData], [SupplyParameterFromSession] in static SSR, and <CacheView> subtree caching |
Flash messages in SSR and caching expensive rendered UI fragments |
Blazor Virtualize |
Variable item heights, AnchorMode, InitialItemIndex, ScrollToItemAsync, and OverscanCount default raised from 3 to 15 |
Smooth chat timelines and variable-height activity feeds |
| Blazor hosting & Server | BasePath, RelativeToCurrentUri, EnvironmentView, WithBrowserOptions, WASM IHostedService, Gateway dev server, and server-triggered circuit pause |
Less JS interop boilerplate and lower Blazor Server memory usage on idle tabs |
| Experimental | Microsoft.AspNetCore.Components.AI and Device Bound Session Credentials (DBSC) |
Prerelease/experimental throughout .NET 11—explore in prototypes, don't ship to prod |
C# 15 native union types also serialize out of the box anywhere System.Text.Json runs—Minimal API request/response bodies, SignalR JSON hubs, and Blazor interop (emitting an anyOf schema in OpenAPI, though route, query, header, and form binding aren't supported). For deeper architectural edge cases on unions, check out our Top 20 C# 15 and .NET 11 Senior Interview Questions.
// C# 15 union type returned directly from an ASP.NET Core 11 Minimal API
public union UnionIntString(int, string);
app.MapGet("/value", () => new UnionIntString(42));
Official Microsoft Video Walkthrough: ASP.NET Core 11 New Features
Watch Daniel Roth and the Microsoft ASP.NET Core team demonstrate async Minimal API validation, automatic CSRF protection, C# 15 union serialization, and OpenAPI 3.2 in .NET 11:
Part 4: ASP.NET Core 11 Breaking Changes
ConcurrencyLimiter Middleware Removed
The problem (.NET 10): Microsoft.AspNetCore.ConcurrencyLimiter has been marked obsolete since .NET 8 (ASP0025), nudging teams toward the built-in rate-limiting middleware—yet plenty of codebases ignored the compiler warning because the package still worked at runtime.
What changed: The source code is deleted in .NET 11 and no 11.x NuGet package exists. UseConcurrencyLimiter, AddQueuePolicy, and AddStackPolicy fail to compile on net11.0, and explicit 11.x package references fail NuGet restore. Migrate to AddRateLimiter:
// Before (.NET 10): compiled with obsolete warning ASP0025
builder.Services.AddQueuePolicy(o =>
{
o.MaxConcurrentRequests = 2;
o.RequestQueueLimit = 25;
});
app.UseConcurrencyLimiter();
// After (.NET 11): built-in rate-limiting middleware with a concurrency policy
builder.Services.AddRateLimiter(o =>
{
o.AddConcurrencyLimiter("heavy-endpoints", limiter =>
{
limiter.PermitLimit = 2;
limiter.QueueLimit = 25;
limiter.QueueProcessingOrder = QueueProcessingOrder.OldestFirst;
});
});
app.UseRateLimiter();
app.MapGet("/reports", () => "ok").RequireRateLimiting("heavy-endpoints");
Global vs. Endpoint Scope Trap:
UseConcurrencyLimiter() throttled every downstream request in the middleware pipeline automatically, whereas .RequireRateLimiting("...") only throttles endpoints you opt in! If you want pipeline-wide throttling, configure o.GlobalLimiter = PartitionedRateLimiter.Create<HttpContext, string>(...) and load-test your 503/429 rejection status codes before shipping.
The Rest of the ASP.NET Core 11 Breaking Changes
| Breaking / Behavioral Change | Category | What to Verify |
|---|---|---|
| Hosting emits OpenTelemetry HTTP tags by default | Behavioral | Duplicate span tags, resolved http.route values, 5xx metric cardinality |
| Blazor SSR defers antiforgery validation to middleware | Behavioral | app.UseAntiforgery() token generation, CORS on legitimate cross-origin posts |
OpenApiVersion defaults to OpenApi3_2 |
Behavioral | Swagger UI, Scalar, API gateways, and client code generators |
Microsoft.OpenApi upgraded to 3.x |
Source | Custom schema/operation transformers (JsonNode, IOpenApiSchema) |
OpenAPI preserves all ProducesResponseType entries per status |
Behavioral | OpenAPI snapshot tests and union/anyOf types in generated clients |
| Kestrel tightens HTTP/1.1, HTTP/2 & HTTP/3 protocol compliance | Behavioral | Rejects Connection, Proxy-Connection, Keep-Alive, non-trailers TE, Transfer-Encoding, and Upgrade on HTTP/2 & HTTP/3; closes HTTP/1.1 connections sending both Content-Length and Transfer-Encoding. Audit reverse proxies! |
QuickGrid sorting and pagination render as <a> links |
Behavioral | Custom CSS rules previously targeting <button> elements |
WebAssemblyHostBuilder loads environment variables into IConfiguration |
Behavioral | Variables like API_ENDPOINT are now included in Blazor WASM configuration |
Additional items on the official breaking-change roster: Blazor custom event registration throwing when a name collides with a native browser event; Blazor enhanced navigation stopping automatic resource preloading; HttpSys exposing the TLS channel binding token by default; legacy MVC compatibility options removed; obsolete Blazor, ImageTagHelper, and ModelMetadataIdentity.ForProperty members removed; Identity passkey sign-in enforcing email/phone confirmation and account lockout; response compression always emitting Vary: Accept-Encoding; OpenAPI server URLs dropping the trailing slash when PathBase is empty; and SqlClient Active Directory authentication moving to its separate extension package.
.NET 11 Production Upgrade Checklist (EF Core & ASP.NET Core)
Work through these eleven steps in order on a dedicated migration branch:
- Bump SDK & packages: Update
global.json, set<TargetFramework>net11.0</TargetFramework>, and upgrade allMicrosoft.AspNetCore.*,Microsoft.EntityFrameworkCore.*,Microsoft.Extensions.*, andSystem.Net.Http.Jsonpackages to11.0.0or newer. - Audit SQL Server compatibility & Entra auth: Check
sys.databasesforcompatibility_level < 160(addUseCompatibilityLevel(150)if needed) and installMicrosoft.Data.SqlClient.Extensions.Azurewherever Entra ID / Managed Identity is used. - Fix
SqlVector<T>reads: Search your solution for reads of vector properties on materialized entities and convert them to explicit.Select(...)projections. - Lock down Cosmos DB apps: Convert every remaining sync call (including
EnsureCreated()) to async, map all document fields written by external services so data isn't lost on save, check composite keys for/ \ ? #, and guard optional projections againstundefined. - Clean up EF models & tooling: Verify
Migrate()calls on migration-less databases, add a directMicrosoft.EntityFrameworkCore.Designreference if needed, migrate keyless owned JSON collections toComplexCollection, and switch collection configuration fromProperty()toPrimitiveCollection(). - Harden split queries: Wrap
AsSplitQuery()callers in a retry strategy, snapshot/serializable transaction, orAsSingleQuery()to handleDbQueryConcurrencyException. - Update Minimal APIs & OpenAPI: Replace
ConcurrencyLimiterwithUseRateLimiter(), ensure cross-assembly Minimal API projects invokeAddValidation()inside the endpoints assembly, updateMicrosoft.OpenApi3.x transformers, and pinOpenApi3_1if downstream consumers aren't ready for 3.2. - Verify OpenTelemetry pipelines: Upgrade
OpenTelemetry.Instrumentation.AspNetCoreto avoid duplicate HTTP tags, check 5xx metric cardinality budgets, and update dashboards grouping onhttp.route. - Review Blazor & Kestrel edge security: Keep
app.UseAntiforgery()during the initial upgrade, configure CORS for valid cross-origin form posts, and test reverse proxies in front of Kestrel for forbidden HTTP/2 or HTTP/3 connection headers. - Run full SQL & snapshot test passes: Run integration suites with SQL logging enabled, compare execution plans on top queries, and refresh OpenAPI and SQL snapshot assertions.
- Canary in staging first: Deploy to staging and monitor 400/500 error rates, query latency, Managed Identity logins, and metric series counts before promoting to production.
Frequently Asked Questions: EF Core 11 & ASP.NET Core 11
Can I run EF Core 11 on .NET 10 or .NET 8?
No. EF Core 11 requires both the .NET 11 SDK to build and the .NET 11 runtime (net11.0) to execute. It does not target .NET Standard, .NET 10, or .NET Framework.
Why are my SqlVector properties null after upgrading to EF Core 11?
In EF Core 11, SqlVector<T> columns are excluded from default entity SELECT statements to avoid transferring large float arrays on routine queries. To load the vector values in C#, project the property explicitly using .Select(d => new { d.Id, d.Embedding }).
What replaced UseConcurrencyLimiter in ASP.NET Core 11?
The obsolete Microsoft.AspNetCore.ConcurrencyLimiter package was removed in ASP.NET Core 11. Replace it with the built-in rate-limiting middleware via builder.Services.AddRateLimiter(o => o.AddConcurrencyLimiter(...)) and app.UseRateLimiter().
How do I switch ASP.NET Core 11 OpenAPI back to version 3.1?
ASP.NET Core 11 defaults to OpenAPI 3.2 (OpenApiSpecVersion.OpenApi3_2). If your client generator or API gateway only supports 3.1, pin it inside Program.cs using builder.Services.AddOpenApi(o => o.OpenApiVersion = OpenApiSpecVersion.OpenApi3_1);.
Official Microsoft Learn Sources
- What's New in EF Core 11
- Breaking changes in EF Core 11
- Provider-facing changes in EF Core 11
- Vector search in the SQL Server EF Core provider
- NativeAOT support and precompiled queries (experimental)
- What's new in ASP.NET Core in .NET 11
- Breaking changes in ASP.NET Core 11
- ConcurrencyLimiter middleware removed
- Hosting emits OpenTelemetry HTTP semantic-convention tags by default
- Blazor SSR defers antiforgery validation to middleware
- OpenApiVersion defaults to OpenApi3_2
- Microsoft.OpenApi upgraded to 3.x
- OpenAPI document includes all ProducesResponseType entries per status code
- Kestrel tightens HTTP protocol compliance
- Validation in ASP.NET Core
- Migrate from ASP.NET Core in .NET 10 to .NET 11
