dotnet-recommended
0
总安装量
6
周安装量
安装命令
npx skills add https://github.com/idotta/skills --skill dotnet-recommended
Agent 安装分布
opencode
6
github-copilot
6
codex
6
gemini-cli
6
kimi-cli
5
Skill 文档
.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 |
|---|---|
| general.md | Local development workflow, migrations, coding philosophy, general rules |
| csharp.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 | Mediator, FluentValidation, ErrorOr, Polly |
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 |
|---|---|---|
| FluentValidation | FluentValidation.DependencyInjectionExtensions |
Validation |
| ErrorOr | ErrorOr |
Result pattern |
| Polly | Microsoft.Extensions.Http.Resilience |
Resilience |
| Serilog | Serilog.AspNetCore |
Logging |
See libraries.md for usage examples.
Code Philosophy Quick Reference
Don’t favor backwards compatibility, favor:
- Features: Build new functionality and improve the product
- Proper cleanup: Remove deprecated code, simplify architecture, eliminate technical debt
- Fixing root causes: When something breaks, fix the root
General Rules
- Read before edit: Always read existing code before proposing changes
- Preserve patterns: Follow existing code style and conventions
- Don’t over-engineer: Keep changes minimal and focused
- Never push: Never push to remote