Hands-on .NET 10 Web API showcasing the most impactful EF Core 10 features: LeftJoin/RightJoin LINQ operators, named query filters, ExecuteUpdateAsync delegate setters, and JSON column mapping — all in a single runnable project.
If this sample saved you time, consider joining our Patreon community. You'll get exclusive .NET tutorials, premium code samples, and early access to new content — all for the price of a coffee.
👉 Join CodingDroplets on Patreon
Prefer a one-time tip? Buy us a coffee ☕
What's New in EF Core 10: Features Every .NET Developer Should Adopt in 2026
This project accompanies the article above. Reading it alongside the code will give you a deeper understanding of why each feature exists and when to use it.
- LeftJoin / RightJoin LINQ operators — EF Core 10 promotes left/right joins to first-class LINQ citizens, replacing the verbose
GroupJoin+DefaultIfEmptypattern - Named query filters — Apply independent, named soft-delete filters per entity and selectively bypass individual filters with
IgnoreQueryFilters() - ExecuteUpdateAsync delegate setters — Bulk-update rows in a single round-trip using strongly-typed property setters without loading entities into memory
- JSON column mapping — Persist complex CLR types (
List<string>) as JSON arrays in a single database column using value converters - ASP.NET Core Web API with .NET 10 — Controller-based API with built-in OpenAPI + Scalar UI, no Swashbuckle required
HTTP Request
│
▼
┌─────────────────────────────┐
│ ProductsController │ ← ApiController (route: api/products)
│ GET / │ → active products only (query filter)
│ GET /left-join │ → EF Core 10 LeftJoin operator
│ GET /include-deleted │ → IgnoreQueryFilters() bypass
│ PUT /{id}/price-increase │ → ExecuteUpdateAsync bulk update
└────────────┬────────────────┘
│
▼
┌─────────────────────────────┐
│ AppDbContext │ ← EF Core 10 DbContext
│ Categories (soft-delete) │
│ Products (soft-delete │
│ + JSON Tags) │
└────────────┬────────────────┘
│
▼
SQLite (app.db)
| Feature | Class / Method | What It Replaces |
|---|---|---|
LeftJoin LINQ operator |
QueryableExtensions.LeftJoin() |
GroupJoin + DefaultIfEmpty |
| Named query filters | HasQueryFilter(c => ...) |
Un-named global filters |
| Selective filter bypass | IgnoreQueryFilters() |
IgnoreQueryFilters() (was all-or-nothing) |
| ExecuteUpdateAsync setters | .ExecuteUpdateAsync(s => s.SetProperty(...)) |
Manual entity load + SaveChanges |
| JSON value converter | HasConversion(serialize, deserialize) |
Separate join table or CSV string |
efcore-10-whats-new/
├── efcore-10-whats-new.sln
└── EFCore10Demo/
├── Controllers/
│ └── ProductsController.cs # All 4 demo endpoints
├── Data/
│ ├── AppDbContext.cs # EF Core DbContext with query filters + JSON mapping
│ └── DatabaseSeeder.cs # Seeds categories + products (incl. soft-deleted)
├── Models/
│ ├── Category.cs # Category entity with IsDeleted flag
│ └── Product.cs # Product entity with Tags (JSON column)
├── Properties/
│ └── launchSettings.json # Scalar UI auto-launch on run
├── appsettings.json # SQLite connection string
├── appsettings.Development.json
├── EFCore10Demo.csproj
└── Program.cs # Minimal hosting with OpenAPI + Scalar
| Requirement | Version |
|---|---|
| .NET SDK | 10.0+ |
| Any IDE | Visual Studio 2022 v17.12+, Rider, or VS Code with C# Dev Kit |
| SQLite | Bundled via Microsoft.EntityFrameworkCore.Sqlite — no install needed |
# 1. Clone the repo
git clone https://github.com/codingdroplets/efcore-10-whats-new.git
cd efcore-10-whats-new
# 2. Build
dotnet build -c Release
# 3. Run (database is created and seeded automatically on first run)
cd EFCore10Demo
dotnet run
# 4. Open Scalar API UI
# Navigate to: http://localhost:5289/scalar/v1The SQLite database (app.db) is created automatically on startup and seeded with sample categories and products — including some soft-deleted records so you can see the query filters in action.
EF Core 10 introduces named query filters. Each entity gets its own independently-bypassable filter:
// Data/AppDbContext.cs
modelBuilder.Entity<Category>().HasQueryFilter(c => !c.IsDeleted);
modelBuilder.Entity<Product>().HasQueryFilter(p => !p.IsDeleted);Every LINQ query now automatically appends WHERE IsDeleted = 0 — you never accidentally expose deleted data.
Before EF Core 10, left joins required verbose GroupJoin + DefaultIfEmpty. Now it's a single operator:
// Controllers/ProductsController.cs — GetLeftJoin()
var result = await context.Categories
.LeftJoin(
context.Products,
category => category.Id,
product => product.CategoryId,
(category, product) => new
{
Category = category.Name,
Product = product != null ? product.Name : "— No Products —"
})
.ToListAsync();The "Furniture" category is soft-deleted and therefore excluded by the query filter — perfect for demonstrating that both features compose cleanly.
The include-deleted endpoint demonstrates bypassing query filters to return all records, including soft-deleted ones:
// Controllers/ProductsController.cs — GetIncludingDeleted()
var products = await context.Products
.IgnoreQueryFilters() // bypasses the soft-delete filter
.Include(p => p.Category)
.AsNoTracking()
.ToListAsync();EF Core 10 refines ExecuteUpdateAsync with delegate-based property setters. This issues a single UPDATE statement directly — no entity tracking, no round-trips:
// Controllers/ProductsController.cs — IncreasePriceByCategory()
var affected = await context.Products
.Where(p => p.CategoryId == categoryId)
.ExecuteUpdateAsync(setters => setters
.SetProperty(p => p.Price, p => Math.Round(p.Price * 1.1m, 2)));Product.Tags is a List<string> stored as a JSON array in a single TEXT column using a value converter:
// Data/AppDbContext.cs
modelBuilder.Entity<Product>()
.Property(p => p.Tags)
.HasConversion(
v => JsonSerializer.Serialize(v, (JsonSerializerOptions?)null),
v => JsonSerializer.Deserialize<List<string>>(v, (JsonSerializerOptions?)null) ?? new List<string>()
);This avoids a separate join table while keeping the data queryable and strongly-typed in C#.
| Method | Endpoint | Description | Status |
|---|---|---|---|
GET |
/api/products |
Active products only (soft-delete filter auto-applied) | 200 |
GET |
/api/products/left-join |
EF Core 10 LeftJoin — categories with their products | 200 |
GET |
/api/products/include-deleted |
All products including soft-deleted (IgnoreQueryFilters) |
200 |
PUT |
/api/products/category/{categoryId}/price-increase |
Bulk 10% price increase via ExecuteUpdateAsync |
200 / 404 |
Scalar UI: Run the project and navigate to
http://localhost:5289/scalar/v1to explore and test all endpoints interactively.
This sample focuses on demonstrating EF Core 10 features via runnable API endpoints rather than unit tests. Use the Scalar API UI or any HTTP client to verify behaviour:
| Endpoint | Expected Behaviour |
|---|---|
GET /api/products |
Returns 4 active products (Gaming Chair + Standing Desk excluded) |
GET /api/products/left-join |
2 active categories with their products listed |
GET /api/products/include-deleted |
All 6 products including soft-deleted |
PUT /api/products/category/1/price-increase |
Increases Electronics prices by 10% |
| Approach | Code Complexity | EF Core Version |
|---|---|---|
GroupJoin + DefaultIfEmpty |
Verbose, 5+ lines | EF Core < 10 |
Query syntax (join ... into grp) |
Readable but wordy | All versions |
LeftJoin() operator |
Clean, single method | EF Core 10+ |
| Approach | Risk of Forgetting | Centralised |
|---|---|---|
Manual WHERE IsDeleted = 0 in every query |
High | No |
| EF Core Query Filters | None (automatic) | Yes |
| Approach | SQL Round-Trips | Memory Usage |
|---|---|---|
| Load entities + SaveChanges | 2 (SELECT + UPDATE) | Loads all rows |
ExecuteUpdateAsync |
1 (direct UPDATE) | Zero — no entity tracking |
- .NET 10 — Target framework
- ASP.NET Core Web API — Controller-based HTTP layer
- EF Core 10.0.5 — ORM with new LINQ operators and filter improvements
- SQLite — Lightweight embedded database (via
Microsoft.EntityFrameworkCore.Sqlite) - Scalar.AspNetCore — Interactive API documentation UI (compatible with .NET 10 built-in OpenAPI)
- Built-in ASP.NET Core OpenAPI —
AddOpenApi()+MapOpenApi()(no Swashbuckle needed)
- What's New in EF Core 10 — Microsoft Docs
- EF Core Query Filters — Microsoft Docs
- ExecuteUpdate and ExecuteDelete — Microsoft Docs
This project is licensed under the MIT License.
| Platform | Link |
|---|---|
| 🌐 Website | https://codingdroplets.com/ |
| 📺 YouTube | https://www.youtube.com/@CodingDroplets |
| 🎁 Patreon | https://www.patreon.com/CodingDroplets |
| ☕ Buy Me a Coffee | https://buymeacoffee.com/codingdroplets |
| 💻 GitHub | http://github.com/codingdroplets/ |
Want more samples like this? Support us on Patreon or buy us a coffee ☕ — every bit helps keep the content coming!