Sitemap
Nick Zdanovych

Nick Zdanovych

Solutions Architect writing about real-world software architecture — .NET, AWS, AI, distributed systems and the trade-offs behind production decisions

Small .NET 10 Features Worth Using in Your Next Project

3 min readOct 26, 2026

--

Less manual validation, simpler queries, and small C# tools without a project file.

Press enter or click to view image in full size

When we upgrade a project to .NET 10, it is easy to continue writing code the same way as before. The application runs on a new version, but we still use the old patterns.

Some smaller features are easy to miss. Here are five that can help with everyday backend work.

The examples below use .NET 10, ASP.NET Core 10, C# 14, and EF Core 10. Remember to update EF Core packages and check your database provider too. Changing the target framework does not update them automatically.

1. Built-in validation for Minimal APIs

In ASP.NET Core 10, Minimal APIs support request validation with data annotations. Add the Microsoft.Extensions.Validation NuGet package, using a supported 10.x version, and register validation:

using System.ComponentModel.DataAnnotations;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddValidation();
var app = builder.Build();app.MapPost("/products", (CreateProduct request) =>
TypedResults.Ok(new { request.Name, request.Quantity }));
app.Run();public sealed class CreateProduct
{
[Required, StringLength(100)]
public string Name { get; init; } = string.Empty;
[Range(1, 1000)]
public int Quantity { get; init; }
}

If validation fails, the API returns 400 Bad Request before the handler runs. There is no need to repeat the same checks inside the endpoint.

I would use this for required fields, string length, and allowed ranges. Business rules still need separate logic. For example, a valid request does not mean the account is allowed to place an order.

Microsoft documentation: Minimal API validation

2. A simpler way to write left joins

Writing a left join in LINQ often meant combining GroupJoin, SelectMany, and DefaultIfEmpty. It worked, but the query was not always easy to read.

.NET 10 adds LeftJoin and RightJoin. EF Core 10 also supports translating these operators into SQL.

var query = db.Employees.LeftJoin(
db.Departments,
employee => employee.DepartmentId,
department => department.Id,
(employee, department) => new
{
employee.Name,
Department = department == null
? "Unassigned"
: department.Name
});

This query keeps all employees, including those without a department.

For me, the main benefit is readability. Do not assume it makes the query faster. Check the generated SQL, execution plan, and support in your database provider.

Microsoft documentation: EF Core 10 joins

3. Disable one EF Core filter without disabling the others

An entity can have both soft delete and tenant filtering. Sometimes you need to load deleted records, but only for the current tenant.

Get Nick Zdanovych’s stories in your inbox

Join Medium for free to get updates from this writer.

EF Core 10 lets you give each filter a name:

Inside OnModelCreating, with TenantId supplied by the context instance:

modelBuilder.Entity<Document>()
.HasQueryFilter("SoftDelete", d => !d.IsDeleted)
.HasQueryFilter("Tenant", d => d.TenantId == TenantId);

A recovery query can include deleted documents and keep the tenant filter:

var documents = await db.Documents
.IgnoreQueryFilters(["SoftDelete"])
.ToListAsync(cancellationToken);

Be careful with IgnoreQueryFilters() without arguments: it disables all filters, including the tenant filter.

For this recovery query, disable only SoftDelete. The tenant ID should come from trusted application context, and authorization still needs its own checks.

Microsoft documentation: Global query filters

4. Less code for property validation

With C# 14, you can use field to access the backing field generated by the compiler:

public sealed class InventoryItem
{
public int Quantity
{
get;
set => field = value >= 0
? value
: throw new ArgumentOutOfRangeException(nameof(value));
}
}

Previously, this needed a separate _quantity field and an explicit getter. Now the compiler handles that part.

This is useful for a small check like preventing a negative quantity. I would keep more complex operations in methods, especially when they involve I/O or changes to other objects.

Microsoft documentation: The field keyword

5. Run a C# utility without creating a project

Sometimes you just need a small utility. With the .NET 10 SDK, you can run a C# file without creating a .csproj.

Save this as count-lines.cs:

if (args.Length != 1)
{
Console.Error.WriteLine("Usage: count-lines <path>");
return 1;
}
Console.WriteLine(File.ReadLines(args[0]).LongCount());
return 0;

Run it with:

dotnet run --file count-lines.cs -- input.txt

This can be useful for repository utilities or checking a data file.

Keep these scripts outside application project folders to avoid conflicts with implicit compilation. If you add packages, pin their versions. When the tool grows, a regular project is easier to maintain.

Microsoft documentation: File-based apps

For a backend project, I would start with validation and named query filters. They address common situations, and you can introduce them without a large refactoring.

--

--

Solutions Architect (.NET, AWS, AI). 10+ years leading architecture and teams.

See more from me