dotnet-recommended
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
Chinese.NET 10 & C# 14 Best Practices
.NET 10 & C# 14 最佳实践
.NET 10 (LTS, Nov 2025) with C# 14. Covers minimal APIs, not MVC.
.NET 10(长期支持版,2025年11月)搭配C# 14。内容涵盖Minimal APIs,不包含MVC。
Detail Files
详细文档
| File | Topics |
|---|---|
| general.md | Local development workflow, migrations, coding philosophy, general rules |
| csharp.md | Extension blocks, |
| 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 |
| 文件 | 主题 |
|---|---|
| general.md | 本地开发工作流、迁移、编码理念、通用规则 |
| csharp.md | 扩展块、 |
| minimal-apis.md | 验证、TypedResults、过滤器、模块化单体、垂直切片 |
| security.md | JWT认证、CORS、速率限制、OpenAPI安全、中间件顺序 |
| infrastructure.md | Options、弹性、Channels、健康检查、缓存、Serilog、EF Core、键控服务 |
| testing.md | WebApplicationFactory、集成测试、认证测试 |
| anti-patterns.md | HttpClient、DI捕获、阻塞异步、N+1查询 |
| libraries.md | Mediator、FluentValidation、ErrorOr、Polly |
Quick Start
快速开始
xml
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<TargetFramework>net10.0</TargetFramework>
<LangVersion>14</LangVersion>
<Nullable>enable</Nullable>
</PropertyGroup>
</Project>csharp
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();xml
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<TargetFramework>net10.0</TargetFramework>
<LangVersion>14</LangVersion>
<Nullable>enable</Nullable>
</PropertyGroup>
</Project>csharp
var builder = WebApplication.CreateBuilder(args);
// 核心服务
builder.Services.AddValidation();
builder.Services.AddProblemDetails();
builder.Services.AddOpenApi();
// 安全
builder.Services.AddAuthentication().AddJwtBearer();
builder.Services.AddAuthorization();
builder.Services.AddRateLimiter(opts => { /* 详见security.md */ });
// 基础设施
builder.Services.AddHealthChecks();
builder.Services.AddOutputCache();
// 模块
builder.Services.AddUsersModule();
var app = builder.Build();
// 中间件(顺序至关重要 - 详见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
结果 vs 异常
dot
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"];
}dot
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
IOptions 选择
dot
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"];
}dot
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
通道类型
dot
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"];
}dot
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
C# 14 扩展块
csharp
extension<T>(IEnumerable<T> source)
{
public bool IsEmpty => !source.Any();
}csharp
extension<T>(IEnumerable<T> source)
{
public bool IsEmpty => !source.Any();
}.NET 10 Built-in Validation
.NET 10 内置验证
csharp
builder.Services.AddValidation();
app.MapPost("/users", (UserDto dto) => TypedResults.Ok(dto));csharp
builder.Services.AddValidation();
app.MapPost("/users", (UserDto dto) => TypedResults.Ok(dto));TypedResults (Always Use)
TypedResults(始终使用)
csharp
app.MapGet("/users/{id}", async (int id, IUserService svc) =>
await svc.GetAsync(id) is { } user
? TypedResults.Ok(user)
: TypedResults.NotFound());csharp
app.MapGet("/users/{id}", async (int id, IUserService svc) =>
await svc.GetAsync(id) is { } user
? TypedResults.Ok(user)
: TypedResults.NotFound());Module Pattern
模块模式
csharp
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;
}
}csharp
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
HTTP弹性
csharp
builder.Services.AddHttpClient<IApi, ApiClient>()
.AddStandardResilienceHandler();csharp
builder.Services.AddHttpClient<IApi, ApiClient>()
.AddStandardResilienceHandler();Error Handling (RFC 9457)
错误处理(RFC 9457)
csharp
builder.Services.AddProblemDetails();
app.UseExceptionHandler();
app.UseStatusCodePages();csharp
builder.Services.AddProblemDetails();
app.UseExceptionHandler();
app.UseStatusCodePages();MANDATORY Patterns (Always Use These)
强制模式(必须始终使用)
| Task | ✅ ALWAYS Use | ❌ NEVER Use |
|---|---|---|
| Extension members | C# 14 | Traditional |
| Property validation | C# 14 | Manual backing fields |
| Null assignment | | |
| API returns | | |
| Options validation | | Missing validation |
| HTTP resilience | | Manual Polly configuration |
| Timestamps | | |
| 任务 | ✅ 始终使用 | ❌ 切勿使用 |
|---|---|---|
| 扩展成员 | C# 14 | 传统 |
| 属性验证 | C# 14 | 手动后备字段 |
| 空赋值 | | |
| API返回 | | |
| Options验证 | | 缺少验证 |
| HTTP弹性 | | 手动Polly配置 |
| 时间戳 | | |
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() │
└─────────────────────────────────────────────────────────────────┘┌─────────────────────────────────────────────────────────────────┐
│ .NET 10 / C# 14 模式 │
├─────────────────────────────────────────────────────────────────┤
│ 扩展属性: extension<T>(IEnumerable<T> s) { │
│ public bool IsEmpty => !s.Any(); │
│ } │
├─────────────────────────────────────────────────────────────────┤
│ FIELD关键字: public string Name { │
│ get => field; │
│ set => field = value?.Trim(); │
│ } │
├─────────────────────────────────────────────────────────────────┤
│ OPTIONS验证: .BindConfiguration(Section) │
│ .ValidateDataAnnotations() │
│ .ValidateOnStart(); // 至关重要! │
├─────────────────────────────────────────────────────────────────┤
│ HTTP弹性: .AddStandardResilienceHandler(); │
├─────────────────────────────────────────────────────────────────┤
│ 类型化结果: TypedResults.Ok(data) │
│ TypedResults.NotFound() │
│ TypedResults.Created(uri, data) │
├─────────────────────────────────────────────────────────────────┤
│ 错误模式: ErrorOr<User> 或 user?.Match(...) │
├─────────────────────────────────────────────────────────────────┤
│ IOPTIONS: IOptions<T> → 启动时加载,不重载 │
│ IOptionsSnapshot<T> → 每次请求重载 │
│ IOptionsMonitor<T> → 实时加载 + OnChange() │
└─────────────────────────────────────────────────────────────────┘Anti-Patterns Quick Reference
反模式快速参考
| Anti-Pattern | Fix |
|---|---|
| Inject |
| |
| Manual Polly config | |
| Singleton → Scoped | Use |
| |
| Exceptions for flow | Use |
| |
Missing | Always add to Options registration |
See anti-patterns.md for complete list.
| 反模式 | 修复方案 |
|---|---|
| 注入 |
| |
| 手动Polly配置 | |
| 单例→作用域 | 使用 |
| |
| 用异常控制流程 | 使用 |
| |
缺少 | 始终在Options注册时添加 |
完整列表请查看anti-patterns.md。
Libraries Quick Reference
库快速参考
| Library | Package | Purpose |
|---|---|---|
| FluentValidation | | Validation |
| ErrorOr | | Result pattern |
| Polly | | Resilience |
| Serilog | | Logging |
See libraries.md for usage examples.
| 库 | 包 | 用途 |
|---|---|---|
| FluentValidation | | 验证 |
| ErrorOr | | 结果模式 |
| Polly | | 弹性 |
| Serilog | | 日志 |
使用示例请查看libraries.md。
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
- 先读再改: 提出更改前务必先阅读现有代码
- 遵循模式: 遵循现有代码风格和约定
- 不要过度设计: 保持更改最小且聚焦
- 切勿直接推送: 切勿推送到远程仓库