Every EF Core release brings a mix of quality-of-life improvements and features you did not know you needed. EF Core 11 — shipping with .NET 11 in November 2026 — is no different, but a few of the changes in the early previews are worth paying attention to now. One-step migrations cut a command from your inner loop. Full-text search indexes can finally be managed through migrations instead of raw SQL scripts. Complex types work with TPT and TPC inheritance for the first time. And if you are still calling SaveChanges() on a Cosmos DB context, EF Core 11 will not let you.

Two previews have landed so far, and while the feature set will grow through the year, there is already enough to dig into. Let us walk through what has shipped, what it means for your projects, and where the sharp edges are.

One-step migrations

If you have ever typed dotnet ef migrations add followed immediately by dotnet ef database update, you have performed a two-step ritual that could have been one step. EF Core 11 merges them with the --add flag on database update:

terminal
dotnet ef database update CreateOrdersTable --add

This scaffolds a new migration named CreateOrdersTable, compiles it using Roslyn at runtime, and applies it to the database in a single invocation. The migration files are still written to disk, so they go into source control as normal. You can pass the same options you would use with migrations add:

terminal
dotnet ef database update AddProductCategories --add --output-dir Migrations/Catalog --namespace MyApp.Migrations.Catalog

In the Package Manager Console, the equivalent is:

script.ps1
Update-Database -Migration CreateOrdersTable -Add

// TIP

The runtime compilation uses Roslyn behind the scenes, which means this works in environments where you cannot recompile the project — useful for .NET Aspire and containerised workflows where the SDK is available but a full build is not practical.

This is a small change, but small changes that shave friction off a loop you run dozens of times a day add up. If you are working on a feature branch and iterating on schema changes, this removes one command and one context switch from every cycle.

Offline migration removal

The migrations remove command also gained a --offline flag:

terminal
dotnet ef migrations remove --offline

This skips the database connection check entirely, removing the migration purely from the file system. Useful when the database is inaccessible — perhaps you are on a train, or the migration was never applied in the first place.

Both migrations remove and database drop now accept a --connection parameter too, so you can target a specific database without changing your DbContext configuration:

terminal
dotnet ef migrations remove --connection "Server=staging;Database=Orders;..."
dotnet ef database drop --connection "Server=test;Database=Orders;..." --force

Complex types with TPT and TPC inheritance

Complex types and JSON columns have been available for several versions, but they had a blind spot: they did not work on entities mapped with Table-Per-Type (TPT) or Table-Per-Concrete-Type (TPC) inheritance. If your domain model combined inheritance hierarchies with value objects, you had to choose between the inheritance mapping you wanted and the complex type support you needed.

EF Core 11 removes that restriction. Consider an inheritance hierarchy with a complex type:

Models/Animal.cs
public abstract class Animal
{
    public int Id { get; set; }
    public string Name { get; set; }
    public required AnimalDetails Details { get; set; }
}

public class Dog : Animal
{
    public string Breed { get; set; }
}

public class Cat : Animal
{
    public bool IsIndoor { get; set; }
}

[ComplexType]
public class AnimalDetails
{
    public DateTime BirthDate { get; set; }
    public string? Veterinarian { get; set; }
}

With a TPT mapping strategy:

Data/AppDbContext.cs
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    modelBuilder.Entity<Animal>().UseTptMappingStrategy();
}

EF Core 11 creates Details_BirthDate and Details_Veterinarian columns on the Animal table, exactly where you would expect them. JSON columns work too — just add .ComplexProperty(a => a.Details, b => b.ToJson()) and the complex type is stored as a JSON column instead.

This is particularly relevant if you are using domain-driven design patterns where value objects (modelled as complex types) sit on base entity types in an inheritance hierarchy. Previously you would have had to flatten the hierarchy or fall back to owned entities with their identity overhead.

MaxBy and MinBy translation

EF Core now translates MaxByAsync and MinByAsync to SQL. These LINQ methods return the element with the maximum or minimum value for a given key selector — not the value itself, but the entire entity.

Services/BlogAnalyticsService.cs
public async Task<Blog?> GetMostPopularBlogAsync(AppDbContext context)
{
    return await context.Blogs.MaxByAsync(b => b.Posts.Count());
}

This translates to:

query.sql
SELECT TOP(1) [b].[Id], [b].[Name]
FROM [Blogs] AS [b]
ORDER BY (
    SELECT COUNT(*)
    FROM [Posts] AS [p]
    WHERE [b].[Id] = [p].[BlogId]) DESC

Before this, you would write .OrderByDescending(b => b.Posts.Count()).FirstOrDefaultAsync() — which works, but MaxByAsync expresses the intent more clearly and matches what you would write in LINQ-to-Objects.

Full-text search gets first-class migration support

SQL Server's full-text search has always required setting up catalogs and indexes outside of EF Core migrations. You would write raw SQL in your migration's Up method or maintain a separate script. EF Core 11 makes full-text catalogs and indexes part of the model:

Data/AppDbContext.cs
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    modelBuilder.HasFullTextCatalog("ftCatalog");

    modelBuilder.Entity<Blog>()
        .HasFullTextIndex(b => b.FullName)
        .HasKeyIndex("PK_Blogs")
        .OnCatalog("ftCatalog");
}

This generates the following SQL in a migration:

query.sql
CREATE FULLTEXT CATALOG [ftCatalog];
CREATE FULLTEXT INDEX ON [Blogs]([FullName]) KEY INDEX [PK_Blogs] ON [ftCatalog];

No more hand-written SQL in migration files. The catalog and index are tracked by the model, so subsequent migrations can detect changes.

Table-valued full-text functions

EF Core has supported the FREETEXT() and CONTAINS() predicates for a while via EF.Functions.FreeText() and EF.Functions.Contains(). These work in Where() clauses for filtering, but they do not give you a relevance ranking.

EF Core 11 adds support for their table-valued counterparts — FREETEXTTABLE() and CONTAINSTABLE() — which return both the matching entity and a ranking score:

Services/SearchService.cs
public async Task<List<SearchResult>> SearchBlogsAsync(AppDbContext context, string query)
{
    return await context.Blogs
        .FreeTextTable(b => b.FullName, query)
        .Select(r => new SearchResult { Blog = r.Value, Rank = r.Rank })
        .OrderByDescending(r => r.Rank)
        .ToListAsync();
}

Both methods return FullTextSearchResult<TEntity>, giving you access to the entity and the SQL Server ranking value. This makes it possible to build search results ordered by relevance without dropping down to raw SQL.

JSON_CONTAINS for SQL Server 2025

If you store primitive collections as JSON columns — a pattern EF Core has supported since version 8 — the Contains query has historically been translated using OPENJSON, which unpacks the entire JSON array into a temporary table and then searches it. SQL Server 2025 introduced JSON_CONTAINS, which checks whether a value exists in a JSON document directly and can leverage a JSON index.

EF Core 11 uses JSON_CONTAINS automatically when you target SQL Server 2025:

Data/AppDbContext.cs
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
    => optionsBuilder
        .UseSqlServer(connectionString, o => o.UseCompatibilityLevel(170));

With that configured, a query like:

Example.cs
var tagged = await context.Posts
    .Where(p => p.Tags.Contains("ef-core"))
    .ToListAsync();

Generates:

query.sql
SELECT [p].[Id], [p].[Title], [p].[Tags]
FROM [Posts] AS [p]
WHERE JSON_CONTAINS([p].[Tags], 'ef-core') = 1

Instead of the previous OPENJSON-based translation. The performance difference is significant for large collections, particularly if you have a JSON index defined on the column.

// NOTE

JSON_CONTAINS does not support searching for null values. EF Core falls back to the OPENJSON translation when it cannot determine that both sides of the comparison are non-nullable.

Cosmos DB overhaul

EF Core 11 brings several improvements to the Azure Cosmos DB provider, all contributed by community member @JoasE.

Transactional batches by default

The provider now uses transactional batches by default when calling SaveChangesAsync. Operations targeting the same container and partition key are grouped into a single batch, providing best-effort atomicity and reducing round trips. You can control this through AutoTransactionBehavior:

Bulk execution

For high-throughput scenarios where you are inserting or updating many documents, bulk execution runs operations in parallel across DbContext instances:

Data/CosmosDbContext.cs
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
    => optionsBuilder.UseCosmos(
        connectionString,
        databaseName: "OrdersDB",
        options => options.BulkExecutionEnabled());

Session token management

In load-balanced environments with multiple application instances, Cosmos DB's session consistency can produce stale reads when requests hit different instances. EF Core 11 exposes session token management so you can pass tokens between contexts:

Services/OrderService.cs
// After writing, capture the session token
var sessionToken = writeContext.Database.GetSessionToken();

// On a different instance or context, apply it before reading
readContext.Database.UseSessionToken(sessionToken);
var order = await readContext.Orders.FindAsync(orderId);

Enable semi-automatic management in the provider configuration:

Example.cs
options => options.SessionTokenManagementMode(SessionTokenManagementMode.SemiAutomatic)

Complex types in Cosmos DB

Complex types are now fully supported in the Cosmos DB provider. They are embedded as nested JSON objects within the owning document, with full query, insert, and update support. Complex types are a better fit than owned types for embedded data in Cosmos DB documents — they have value semantics and no identity, which aligns more naturally with how embedded objects work in a document database.

Breaking changes to watch for

Sync I/O is gone from the Cosmos DB provider

This is the most impactful breaking change. Since EF Core 9, synchronous I/O on the Cosmos DB provider has been deprecated with an opt-in to suppress the warning. In EF Core 11, the opt-in is removed entirely. Calling ToList(), SaveChanges(), or any synchronous method on a Cosmos DB context will throw — no configuration switch, no escape hatch.

Example.cs
// Bad — throws InvalidOperationException in EF Core 11
var orders = context.Orders.ToList();

// Good — use async methods
var orders = await context.Orders.ToListAsync();

If you have Cosmos DB code that still uses synchronous APIs, this upgrade will force the conversion. The underlying reason is sound — the Cosmos DB SDK is async-only, and the previous "sync-over-async" bridge could deadlock under load.

SQLitePCLRaw 3.0 package changes

If you use Microsoft.Data.Sqlite with encryption (SQLCipher), the SQLitePCLRaw.bundle_e_sqlcipher package has been removed in SQLitePCLRaw 3.0. You will need to switch to a professionally maintained alternative like the official SQLite Encryption Extension (SEE), Zetetic's SQLCipher builds, or SQLite3 Multiple Ciphers.

Several other bundle packages (bundle_sqlite3, bundle_winsqlite3, bundle_green) have also been removed. If you depend on these, replace them with the corresponding provider package and add explicit initialisation code. The default bundle_e_sqlite3 is unaffected — just update the version number.

Common pitfalls

Summary

EF Core 11 is shaping up as a release focused on removing long-standing limitations rather than introducing fundamentally new concepts:

The previews are available now via the .NET 11 SDK. If you are not ready to upgrade, the full-text search and migration improvements alone are worth keeping an eye on for when .NET 11 goes GA in November.