.NET 10 & C# 14 Best Practices
.NET 10 (LTS, Nov 2025) with C# 14. Covers minimal APIs, not MVC.
Official docs: .NET 10 | C# 14 | ASP.NET Core 10
Detail Files
File Topics
csharp-14.md Extension blocks, field keyword, null-conditional assignment
minimal-apis.md Validation, TypedResults, filters, modular monolith, vertical slices
security.md JWT auth, CORS, rate limiting, OpenAPI security, middleware order
infrastructure.md Options, resilience, channels, health checks, caching, Serilog, EF Core, keyed services
testing.md WebApplicationFactory, integration tests, auth testing
anti-patterns.md HttpClient, DI captive, blocking async, N+1 queries
libraries.md MediatR, FluentValidation, Mapster, ErrorOr, Polly, Aspire
Quick Start
var builder = WebApplication.CreateBuilder(args);
// Core services builder.Services.AddValidation(); builder.Services.AddProblemDetails(); builder.Services.AddOpenApi();
// Security builder.Services.AddAuthentication().AddJwtBearer(); builder.Services.AddAuthorization(); builder.Services.AddRateLimiter(opts => { / see security.md / });
// Infrastructure builder.Services.AddHealthChecks(); builder.Services.AddOutputCache();
// Modules builder.Services.AddUsersModule();
var app = builder.Build();
// Middleware (ORDER MATTERS - see security.md) app.UseExceptionHandler(); app.UseHttpsRedirection(); app.UseCors(); app.UseRateLimiter(); app.UseAuthentication(); app.UseAuthorization(); app.UseOutputCache();
app.MapOpenApi(); app.MapHealthChecks("/health"); app.MapUsersEndpoints(); app.Run();
Decision Flowcharts
Result vs Exception
digraph {
"Error type?" [shape=diamond];
"Expected?" [shape=diamond];
"Result
IOptions Selection
digraph {
"Runtime changes?" [shape=diamond];
"Per-request?" [shape=diamond];
"IOptions
Channel Type digraph { "Trust producer?" [shape=diamond]; "Can drop?" [shape=diamond]; "Bounded+Wait" [shape=box,style=filled,fillcolor=lightgreen]; "Bounded+Drop" [shape=box]; "Unbounded" [shape=box]; "Trust producer?" -> "Unbounded" [label="yes"]; "Trust producer?" -> "Can drop?" [label="no"]; "Can drop?" -> "Bounded+Drop" [label="yes"]; "Can drop?" -> "Bounded+Wait" [label="no"]; }
Key Patterns Summary
C# 14 Extension Blocks
extension
.NET 10 Built-in Validation builder.Services.AddValidation(); app.MapPost("/users", (UserDto dto) => TypedResults.Ok(dto));
TypedResults (Always Use) app.MapGet("/users/{id}", async (int id, IUserService svc) => await svc.GetAsync(id) is { } user ? TypedResults.Ok(user) : TypedResults.NotFound());
Module Pattern
public static class UsersModule
{
public static IServiceCollection AddUsersModule(this IServiceCollection s) => s
.AddScoped
public static IEndpointRouteBuilder MapUsersEndpoints(this IEndpointRouteBuilder app)
{
var g = app.MapGroup("/api/users").WithTags("Users");
g.MapGet("/{id}", GetUser.Handle);
return app;
}
}
HTTP Resilience
builder.Services.AddHttpClient
Error Handling (RFC 9457) builder.Services.AddProblemDetails(); app.UseExceptionHandler(); app.UseStatusCodePages();
MANDATORY Patterns (Always Use These)
Task ✅ ALWAYS Use ❌ NEVER Use
Extension members C# 14 extension
Anti-Patterns Quick Reference
Anti-Pattern Fix
new HttpClient() Inject HttpClient or IHttpClientFactory
Results.Ok() TypedResults.Ok()
Manual Polly config AddStandardResilienceHandler()
Singleton → Scoped Use IServiceScopeFactory
GetAsync().Result await GetAsync()
Exceptions for flow Use ErrorOr
See anti-patterns.md for complete list.
Libraries Quick Reference Library Package Purpose MediatR MediatR CQRS FluentValidation FluentValidation.DependencyInjectionExtensions Validation Mapster Mapster.DependencyInjection Mapping ErrorOr ErrorOr Result pattern Polly Microsoft.Extensions.Http.Resilience Resilience Serilog Serilog.AspNetCore Logging
See libraries.md for usage examples.