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.
Official docs: .NET 10 | C# 14 | ASP.NET Core 10
.NET 10(长期支持版,2025年11月)搭配C# 14。内容涵盖Minimal APIs,不包含MVC。
官方文档: .NET 10 | C# 14 | ASP.NET Core 10

Detail Files

详细文档

FileTopics
general.mdLocal development workflow, migrations, coding philosophy, general rules
csharp.mdExtension blocks,
field
keyword, null-conditional assignment
minimal-apis.mdValidation, TypedResults, filters, modular monolith, vertical slices
security.mdJWT auth, CORS, rate limiting, OpenAPI security, middleware order
infrastructure.mdOptions, resilience, channels, health checks, caching, Serilog, EF Core, keyed services
testing.mdWebApplicationFactory, integration tests, auth testing
anti-patterns.mdHttpClient, DI captive, blocking async, N+1 queries
libraries.mdMediator, FluentValidation, ErrorOr, Polly

文件主题
general.md本地开发工作流、迁移、编码理念、通用规则
csharp.md扩展块、
field
关键字、空条件赋值
minimal-apis.md验证、TypedResults、过滤器、模块化单体、垂直切片
security.mdJWT认证、CORS、速率限制、OpenAPI安全、中间件顺序
infrastructure.mdOptions、弹性、Channels、健康检查、缓存、Serilog、EF Core、键控服务
testing.mdWebApplicationFactory、集成测试、认证测试
anti-patterns.mdHttpClient、DI捕获、阻塞异步、N+1查询
libraries.mdMediator、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 membersC# 14
extension<T>()
blocks
Traditional
this
extension methods
Property validationC# 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

任务✅ 始终使用❌ 切勿使用
扩展成员C# 14
extension<T>()
传统
this
扩展方法
属性验证C# 14
field
关键字
手动后备字段
空赋值
obj?.Prop = value
if (obj != null) obj.Prop = value
API返回
TypedResults.Ok()
Results.Ok()
Options验证
.ValidateOnStart()
缺少验证
HTTP弹性
AddStandardResilienceHandler()
手动Polly配置
时间戳
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()    │
└─────────────────────────────────────────────────────────────────┘

┌─────────────────────────────────────────────────────────────────┐
│                    .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-PatternFix
new HttpClient()
Inject
HttpClient
or
IHttpClientFactory
Results.Ok()
TypedResults.Ok()
Manual Polly config
AddStandardResilienceHandler()
Singleton → ScopedUse
IServiceScopeFactory
GetAsync().Result
await GetAsync()
Exceptions for flowUse
ErrorOr<T>
Result pattern
DateTime.Now
DateTime.UtcNow
Missing
.ValidateOnStart()
Always add to Options registration
See anti-patterns.md for complete list.

反模式修复方案
new HttpClient()
注入
HttpClient
IHttpClientFactory
Results.Ok()
TypedResults.Ok()
手动Polly配置
AddStandardResilienceHandler()
单例→作用域使用
IServiceScopeFactory
GetAsync().Result
await GetAsync()
用异常控制流程使用
ErrorOr<T>
结果模式
DateTime.Now
DateTime.UtcNow
缺少
.ValidateOnStart()
始终在Options注册时添加
完整列表请查看anti-patterns.md

Libraries Quick Reference

库快速参考

LibraryPackagePurpose
FluentValidation
FluentValidation.DependencyInjectionExtensions
Validation
ErrorOr
ErrorOr
Result pattern
Polly
Microsoft.Extensions.Http.Resilience
Resilience
Serilog
Serilog.AspNetCore
Logging
See libraries.md for usage examples.
用途
FluentValidation
FluentValidation.DependencyInjectionExtensions
验证
ErrorOr
ErrorOr
结果模式
Polly
Microsoft.Extensions.Http.Resilience
弹性
Serilog
Serilog.AspNetCore
日志
使用示例请查看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
  • 先读再改: 提出更改前务必先阅读现有代码
  • 遵循模式: 遵循现有代码风格和约定
  • 不要过度设计: 保持更改最小且聚焦
  • 切勿直接推送: 切勿推送到远程仓库