dotnet-10-csharp-14
mhagrelius/dotfiles
Build .NET 10 & C# 14 apps with minimal APIs, modular patterns, and modern best practices.
What is dotnet-10-csharp-14?
Guidance for .NET 10 (LTS) and C# 14 development, covering minimal APIs, modular monolith patterns, security, resilience, and modern language features. Use when building new ASP.NET Core applications, refactoring to feature folders, or adopting C# 14 syntax like extension blocks and the field keyword.
- C# 14 extension blocks, field keyword, and null-conditional assignment patterns
- Minimal APIs with validation, TypedResults, filters, and modular monolith architecture
- Security: JWT auth, CORS, rate limiting, middleware ordering, OpenAPI security
- Infrastructure: Options pattern selection, HTTP resilience, Channels, health checks, caching, Serilog, EF Core
- Decision flowcharts for result vs exception handling, IOptions selection, and Channel types
- Anti-pattern detection: HttpClient instantiation, blocking async, captive dependencies, N+1 queries
How to install dotnet-10-csharp-14
npx skills add https://github.com/mhagrelius/dotfiles --skill dotnet-10-csharp-14How to use dotnet-10-csharp-14
- 1.Review the Quick Start section to scaffold a new .NET 10 project with LangVersion 14 and Nullable enabled
- 2.Choose your architectural pattern: modular monolith, vertical slices, or feature folders using the Module Pattern example
- 3.Use the decision flowcharts to determine error handling strategy (Result vs Exception), Options type, and Channel configuration
- 4.Apply MANDATORY patterns from the reference table: extension blocks, TypedResults, field keyword, Options validation
- 5.Implement security middleware in the correct order: ExceptionHandler → HTTPS → CORS → RateLimiter → Authentication → Authorization
- 6.Configure HTTP resilience with AddStandardResilienceHandler() and add health checks, caching, and problem details
- 7.Reference the detail files (csharp-14.md, minimal-apis.md, security.md, infrastructure.md) for deep dives on specific topics
Use cases
- Building a new ASP.NET Core 10 minimal API with modular feature folders and vertical slices
- Refactoring legacy code to use C# 14 extension blocks and the field keyword for cleaner properties
- Implementing JWT authentication with rate limiting and proper middleware ordering in a secure API
- Setting up HTTP resilience with Polly and configuring Options validation at startup
- Writing integration tests with WebApplicationFactory and testing authenticated endpoints
- Backend developers building .NET 10 applications
- Teams adopting C# 14 language features and modern patterns
- Developers implementing minimal APIs instead of traditional MVC
- Architects designing modular monoliths with feature-based organization
- DevOps/infrastructure engineers configuring resilience, caching, and health checks
dotnet-10-csharp-14 FAQ
Use IOptions<T> for startup-only configuration with no runtime changes. Use IOptionsSnapshot<T> when you need per-request reloading. Use IOptionsMonitor<T> for live configuration changes with OnChange() callbacks. See the IOptions Selection flowchart.
Use ErrorOr<T> or Result<T> for expected domain errors. Throw exceptions only for unexpected infrastructure failures. The Result vs Exception flowchart guides this decision.
C# 14 extension blocks (extension<T>(source) { }) are cleaner, more readable, and the mandatory pattern. Traditional this extension methods are outdated and should not be used.
Middleware executes in declaration order. Incorrect order breaks security: UseExceptionHandler must come first, then HTTPS, CORS, RateLimiter, Authentication, Authorization. See security.md for the full order.
Always use TypedResults.Ok(). It provides better type safety, OpenAPI documentation, and is the modern .NET 10 pattern. Results.Ok() is legacy.
Full instructions (SKILL.md)
Source of truth, from mhagrelius/dotfiles.
name: dotnet-10-csharp-14 description: Use when building .NET 10 or C# 14 applications; when using minimal APIs, modular monolith patterns, or feature folders; when implementing HTTP resilience, Options pattern, Channels, or validation; when seeing outdated patterns like old extension method syntax
.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
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<TargetFramework>net10.0</TargetFramework>
<LangVersion>14</LangVersion>
<Nullable>enable</Nullable>
</PropertyGroup>
</Project>
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<T>/ErrorOr" [shape=box];
"Exception" [shape=box];
"Error type?" -> "Expected?" [label="domain"];
"Error type?" -> "Exception" [label="infrastructure"];
"Expected?" -> "Result<T>/ErrorOr" [label="yes"];
"Expected?" -> "Exception" [label="no"];
}
IOptions Selection
digraph {
"Runtime changes?" [shape=diamond];
"Per-request?" [shape=diamond];
"IOptions<T>" [shape=box];
"IOptionsSnapshot<T>" [shape=box];
"IOptionsMonitor<T>" [shape=box];
"Runtime changes?" -> "IOptions<T>" [label="no"];
"Runtime changes?" -> "Per-request?" [label="yes"];
"Per-request?" -> "IOptionsSnapshot<T>" [label="yes"];
"Per-request?" -> "IOptionsMonitor<T>" [label="no"];
}
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<T>(IEnumerable<T> source)
{
public bool IsEmpty => !source.Any();
}
.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<IUserService, UserService>();
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<IApi, ApiClient>()
.AddStandardResilienceHandler();
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<T>() blocks | Traditional this extension methods |
| Property validation | C# 14 field keyword | Manual backing fields |
| Null assignment | obj?.Prop = value | if (obj != null) obj.Prop = value |
| API returns | TypedResults.Ok() | Results.Ok() |
| Options validation | .ValidateOnStart() | Missing validation |
| HTTP resilience | AddStandardResilienceHandler() | Manual Polly configuration |
| Timestamps | DateTime.UtcNow | DateTime.Now |
Quick Reference Card
┌─────────────────────────────────────────────────────────────────┐
│ .NET 10 / C# 14 PATTERNS │
├─────────────────────────────────────────────────────────────────┤
│ EXTENSION PROPERTY: extension<T>(IEnumerable<T> s) { │
│ public bool IsEmpty => !s.Any(); │
│ } │
├─────────────────────────────────────────────────────────────────┤
│ FIELD KEYWORD: public string Name { │
│ get => field; │
│ set => field = value?.Trim(); │
│ } │
├─────────────────────────────────────────────────────────────────┤
│ OPTIONS VALIDATION: .BindConfiguration(Section) │
│ .ValidateDataAnnotations() │
│ .ValidateOnStart(); // CRITICAL! │
├─────────────────────────────────────────────────────────────────┤
│ HTTP RESILIENCE: .AddStandardResilienceHandler(); │
├─────────────────────────────────────────────────────────────────┤
│ TYPED RESULTS: TypedResults.Ok(data) │
│ TypedResults.NotFound() │
│ TypedResults.Created(uri, data) │
├─────────────────────────────────────────────────────────────────┤
│ ERROR PATTERN: ErrorOr<User> or user?.Match(...) │
├─────────────────────────────────────────────────────────────────┤
│ IOPTIONS: IOptions<T> → startup, no reload │
│ IOptionsSnapshot<T> → per-request reload │
│ IOptionsMonitor<T> → live + OnChange() │
└─────────────────────────────────────────────────────────────────┘
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<T> Result pattern |
DateTime.Now | DateTime.UtcNow |
Missing .ValidateOnStart() | Always add to Options registration |
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.
Related skills
More from mhagrelius/dotfiles and the wider catalog.

chinese-writing
中文写作技能指南,用于生成高质量的周刊、博客及科技资讯类文章。

youtube-transcript
Download YouTube video transcripts when user provides a YouTube URL or asks to download/get/fetch a transcript from YouTube. Also use when user wants to transcribe or get captions/subtitles from a YouTube video.

airunway-aks-setup
Set up AI Runway on AKS from bare cluster to running model in six steps.

analyze-test-run
Agent skill from microsoft/azure-skills.

appinsights-instrumentation
Instrument webapps with Azure Application Insights: SDK setup, telemetry patterns, and configuration references.

azure-ai
Azure AI services skill for Search, Speech, OpenAI, and Document Intelligence in coding agents