What's New in EF Core 11 and ASP.NET Core 11 (.NET 11)

What's new in EF Core 11 and ASP.NET Core 11? Real code, breaking changes that bite in production, and an upgrade checklist for your .NET 11 move.

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).

What's New in EF Core 11 and ASP.NET Core 11 (.NET 11) - New Features, Breaking Changes, and Upgrade Guide
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

Production Heads-Up: If you're upgrading an existing .NET 10 workload this week, audit these seven items before merging your target framework bump.
  1. Cosmos DB drops unmapped JSON properties on save (High impact): Extra fields written by other services are no longer preserved via __jObject.
  2. Microsoft.Data.SqlClient 7.0 splits out Entra ID auth: Managed Identity and Azure AD connection strings fail at runtime unless you install Microsoft.Data.SqlClient.Extensions.Azure.
  3. SQL Server compatibility level defaults to 160: Older SQL Server 2019 (or unrestored compatibility level) databases can hit unsupported SQL syntax like LEAST and GREATEST.
  4. SqlVector<T> properties come back null: Vector columns are excluded from default entity SELECT queries unless you project them explicitly.
  5. OpenAPI documents default to v3.2: Downstream code generators, gateways, and custom Microsoft.OpenApi 3.x transformers may need updates.
  6. ConcurrencyLimiter middleware is gone: Projects calling UseConcurrencyLimiter() won't compile on net11.0; migrate to UseRateLimiter().
  7. 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

SDK & Runtime Requirement: EF Core 11 requires the .NET 11 SDK to build and the .NET 11 runtime to run. It won't run on .NET 10, earlier .NET releases, or .NET Framework. All snippets below target 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, ExecuteUpdate assignments on nested properties, a TPT edge case with ComplexProperty, 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 add on 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 BY clauses 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 to JSON_PATH_EXISTS on SQL Server 2022 and later.
  • When you opt in to SQL Server 2025 via compatibility level 170, Contains over primitive collections stored as JSON translates to JSON_CONTAINS, which can leverage a native JSON index.
  • EF.Functions.JsonContains() lets you invoke JSON_CONTAINS directly 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.

Provider-Facing Only (Structured JsonPath): 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();
2. High-Impact Data Loss Risk — Unmapped Cosmos JSON Properties: In EF Core 10, unmapped JSON fields in a Cosmos document were stashed inside a hidden __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: Calling db.Database.Migrate() on a project with no migrations now throws instead of silently succeeding. Suppress with ConfigureWarnings(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, or AsSingleQuery().
  • Property() rejects primitive collections: Configure List<int> or string[] columns via PrimitiveCollection() instead of Property().
  • Owned JSON collections without keys warn: Replace keyless OwnsMany(..., b => b.ToJson()) mappings with ComplexCollection(b => b.Posts, b => b.ToJson()).
  • Tools packages drop transitive Design reference: Add Microsoft.EntityFrameworkCore.Design directly with PrivateAssets="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 legacy AppContext switch (though note the old escaping could cause key collisions).
  • Cosmos optional projections evaluating to undefined throw: Wrap optional projection paths with EF.Functions.IsDefined(...) or EF.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:

  • ValidatableTypeAttribute and SkipValidationAttribute are now stable—delete your ASP0029 suppressions.
  • Minimal API validation supports end-to-end async rules via AsyncValidationAttribute and IAsyncValidatableObject, running property and collection-element checks concurrently.
  • Calling AddLocalization() alongside AddValidation() enables localized error messages and display names automatically, and custom IStringLocalizerFactory implementations (backed by SQL or JSON) override default .resx files 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), calling AddValidation() only in the host project means invalid payloads will bypass validation and return 200 OK. Expose an extension method inside the endpoints assembly that calls services.AddValidation(), and call that from Program.cs.
  • Database race conditions: Async validation adds a round-trip per request and is only a user-friendly check—always keep a database UNIQUE index 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.OpenApiVersion now defaults to OpenApiSpecVersion.OpenApi3_2 (landed in .NET 11 Preview 6).
  • FileContentResult (along with FileStreamResult, FileContentHttpResult, and FileStreamHttpResult) generates an OpenAPI schema with type: string and format: binary.
  • All declared response types for a single status code are preserved—multiple types sharing one content type combine into an anyOf schema.
  • Microsoft.AspNetCore.OpenApi upgrades to Microsoft.OpenApi 3.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.AspNetCore attach attributes, upgrade the instrumentation package—its latest build detects the framework's tags and skips duplicates.
  • http.route formatting 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.duration adds an error.type dimension on 5xx responses (increasing time-series cardinality), and Activity.StatusDescription is no longer populated on unhandled exceptions (exceptions are still recorded as span events). Note that the AppContext switch only suppresses the tags—it doesn't revert the http.route or StatusDescription changes.

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 like button.col-title. If needed, revert via Microsoft.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:

  1. Bump SDK & packages: Update global.json, set <TargetFramework>net11.0</TargetFramework>, and upgrade all Microsoft.AspNetCore.*, Microsoft.EntityFrameworkCore.*, Microsoft.Extensions.*, and System.Net.Http.Json packages to 11.0.0 or newer.
  2. Audit SQL Server compatibility & Entra auth: Check sys.databases for compatibility_level < 160 (add UseCompatibilityLevel(150) if needed) and install Microsoft.Data.SqlClient.Extensions.Azure wherever Entra ID / Managed Identity is used.
  3. Fix SqlVector<T> reads: Search your solution for reads of vector properties on materialized entities and convert them to explicit .Select(...) projections.
  4. 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 against undefined.
  5. Clean up EF models & tooling: Verify Migrate() calls on migration-less databases, add a direct Microsoft.EntityFrameworkCore.Design reference if needed, migrate keyless owned JSON collections to ComplexCollection, and switch collection configuration from Property() to PrimitiveCollection().
  6. Harden split queries: Wrap AsSplitQuery() callers in a retry strategy, snapshot/serializable transaction, or AsSingleQuery() to handle DbQueryConcurrencyException.
  7. Update Minimal APIs & OpenAPI: Replace ConcurrencyLimiter with UseRateLimiter(), ensure cross-assembly Minimal API projects invoke AddValidation() inside the endpoints assembly, update Microsoft.OpenApi 3.x transformers, and pin OpenApi3_1 if downstream consumers aren't ready for 3.2.
  8. Verify OpenTelemetry pipelines: Upgrade OpenTelemetry.Instrumentation.AspNetCore to avoid duplicate HTTP tags, check 5xx metric cardinality budgets, and update dashboards grouping on http.route.
  9. 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.
  10. 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.
  11. 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

About the author

MOHAMMED KADI
Software Engineer. Passionate about IT certifications, automation, and building scalable tech solutions.

Post a Comment

Welcome to Iwalen.com! If you have any questions or need assistance with any of our resources, feel free to ask. Please keep the discussion professional and avoid posting external links. All comments are moderated to ensure a high-quality community experience.