Build or update the BlueBubbles external channel plugin for Moltbot (extension package, REST...
npx skills add mhagrelius/dotfiles --skill "dotnet-10-csharp-14"
Install specific skill from multi-skill repository
# 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
# SKILL.md
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.
# Supported AI Coding Agents
This skill is compatible with the SKILL.md standard and works with all major AI coding agents:
Learn more about the SKILL.md standard and how to use these skills with your preferred AI coding agent.