EF Core 10 Is INSANE - Overview of Game Changer features

Introduction
Most EF Core releases are a bag of small quality-of-life fixes. EF Core 10 has a few of those too, but it also lands several changes that remove long-standing workarounds: you no longer hand-write a GroupJoin plus SelectMany plus DefaultIfEmpty to get a left join, you no longer have one all-or-nothing global query filter, and you can now do vector similarity search against SQL Server without a bolt-on.
This video runs through the headline features against a small blog/book/author model on .NET 10 and SQL Server. Here is what each one is and when you would reach for it.
🎬 Watch the full video here:
LeftJoin and RightJoin as real operators
Before EF Core 10, a left join in LINQ-to-Entities was the incantation everyone had to look up: GroupJoin, then SelectMany, then DefaultIfEmpty. It worked, it translated fine, and nobody remembered the shape.
EF Core 10 adds LeftJoin and RightJoin as first-class query operators:
await context.Authors
.LeftJoin(context.Books,
author => author.Id,
book => book.AuthorId,
(author, book) => new { author.Name, book.Title })
.ToListAsync();
Same generated SQL as the old pattern, a fraction of the code, and it reads like the join you meant. This is the change you will use most often.
Named query filters
Global query filters (soft delete, multi-tenancy, "only verified rows") have always had one painful limitation: one filter per entity, and IgnoreQueryFilters() turned off all of them. If you had soft-delete and tenant filtering on the same entity, you could not disable one without disabling the other.
EF Core 10 lets you name filters and toggle them individually:
modelBuilder.Entity<Author>()
.HasQueryFilter("SoftDeletionFilter", a => !a.IsDeleted)
.HasQueryFilter("VerifiedFilter", a => a.IsVerified);
// disable just one, keep the rest
await context.Authors
.IgnoreQueryFilters(["SoftDeletionFilter"])
.ToListAsync();
An admin screen that needs to see soft-deleted rows but must still respect tenant isolation is now a one-liner instead of a manual re-implementation of the other filter.
Complex types mapped to JSON
EF Core 10 lets a complex type be stored as a JSON document in a column:
modelBuilder.Entity<Book>()
.ComplexProperty(x => x.Attributes, x => x.ToJson());
BookAttributes (genre, page count, language, a string[] of tags) lives in one nvarchar column as JSON, and you can still query into it in LINQ:
await context.Books
.Where(b => b.Attributes.Pages > 300)
.ToListAsync();
EF Core translates the predicate to SQL JSON path access. This is the pragmatic middle ground between a rigid column-per-field schema and an untyped blob - useful for attribute bags that vary and are not worth their own table.
Vector search for RAG, built in
This is the flashiest one. SQL Server 2025 has a native vector type, and EF Core 10 can map to it and translate distance functions:
[Column(TypeName = "vector(1536)")]
public SqlVector<float> Vector { get; set; }
await context.BlogPosts
.OrderBy(p => EF.Functions.VectorDistance("cosine", p.Vector, queryVector))
.Take(3)
.ToListAsync();
Combined with Microsoft.Extensions.AI to generate embeddings (the demo uses an Azure OpenAI embedding model), you get the retrieval half of a RAG pipeline - "find the three most semantically similar posts to this text" - as an ordinary EF Core query, with no separate vector database.
Also worth knowing
ExecuteUpdateAsynccontinues to expand what it can express, including updates driven by other columns, so more bulk updates avoid loading entities.- Various LINQ translation gaps closed, and better SQL for existing queries - the kind of thing you get for free by upgrading.
Common pitfalls
- Vector search needs the right SQL Server. The native
vectortype is SQL Server 2025 / recent Azure SQL. Older targets do not have it. - Named filters are opt-in by name. If you migrate existing single filters, give them names and update every
IgnoreQueryFilters()call site, or behavior shifts. - JSON-mapped complex types query well but index differently. Filtering
b.Attributes.Pages > 300works, but performance depends on JSON indexing support in your database - do not assume it is free on a large table. LeftJoinresult selectors can yield nulls. The right-side entity is null when there is no match; project defensively.
Key Takeaways
LeftJoin/RightJoinare now first-class operators - retire theGroupJoin+SelectMany+DefaultIfEmptypattern.- Query filters can be named and disabled individually with
IgnoreQueryFilters(["Name"]), fixing the all-or-nothing problem for soft delete plus multi-tenancy. - Complex types can be stored as JSON in a single column and still be queried in LINQ.
- SQL Server's native vector type plus
EF.Functions.VectorDistancegives you similarity search - the retrieval side of RAG - without a dedicated vector store. - Upgrading also brings a batch of translation improvements you do not have to ask for.
Get the Full Source Code
The complete runnable solution - the blog/book/author model, the named query filters, the JSON complex type, and the vector-search endpoints wired to an embedding generator - is available to Patreon supporters. If you want to run the RAG query against real embeddings instead of rebuilding it from the walkthrough above, you can find it on Patreon.