Dotnet Patterns

affaan-m/ECC/docs/zh-CN/skills/dotnet-patterns

by affaan-mef648e01899ba3e8dc6371642deaaf64b4477775No license275K starsListed Oct 9, 2026Updated Oct 9, 2026Repository updated 4 days ago

惯用的C#和.NET模式、约定、依赖注入、async/await以及构建健壮、可维护的.NET应用程序的最佳实践。

Instructions onlySoftware Development
AI-generated overview

Reference guide of idiomatic C# and .NET patterns covering DI, async/await, options, results and EF Core.

What it does
This skill supplies a reference document of idiomatic C# and .NET patterns and conventions for building maintainable applications. It presents guidance with code examples on immutability, explicit design, dependency injection, async/await, the options pattern, the result pattern, EF Core repositories, middleware, minimal APIs and guard clauses. It also lists anti-patterns with suggested fixes. It produces guidance and example code rather than files or scripts.
When to use it
Use it when writing or reviewing new C# code, refactoring an existing .NET application, or designing service architecture with ASP.NET Core. It is meant as a style and pattern reference during .NET development work.
Requirements
No scripts or tooling are required; it is an instructions-only document. Applying the examples assumes a .NET/C# project, and some examples reference ASP.NET Core, EF Core and dependency injection libraries.

.NET 开发模式

用于构建健壮、高性能且可维护应用程序的惯用 C# 和 .NET 模式。

何时激活

  • 编写新的 C# 代码时
  • 审查 C# 代码时
  • 重构现有 .NET 应用程序时
  • 使用 ASP.NET Core 设计服务架构时

核心原则

1. 优先使用不可变性

对数据模型使用记录和仅初始化属性。可变性应作为明确且有理由的选择。

csharp
// Good: Immutable value objectpublic sealed record Money(decimal Amount, string Currency);
// Good: Immutable DTO with init setterspublic sealed class CreateOrderRequest{    public required string CustomerId { get; init; }    public required IReadOnlyList<OrderItem> Items { get; init; }}
// Bad: Mutable model with public setterspublic class Order{    public string CustomerId { get; set; }    public List<OrderItem> Items { get; set; }}

2. 显式优于隐式

明确表达可空性、访问修饰符和意图。

csharp
// Good: Explicit access modifiers and nullabilitypublic sealed class UserService{    private readonly IUserRepository _repository;    private readonly ILogger<UserService> _logger;
    public UserService(IUserRepository repository, ILogger<UserService> logger)    {        _repository = repository ?? throw new ArgumentNullException(nameof(repository));        _logger = logger ?? throw new ArgumentNullException(nameof(logger));    }
    public async Task<User?> FindByIdAsync(Guid id, CancellationToken cancellationToken)    {        return await _repository.FindByIdAsync(id, cancellationToken);    }}

3. 依赖抽象

对服务边界使用接口。通过依赖注入容器注册。

csharp
// Good: Interface-based dependencypublic interface IOrderRepository{    Task<Order?> FindByIdAsync(Guid id, CancellationToken cancellationToken);    Task<IReadOnlyList<Order>> FindByCustomerAsync(string customerId, CancellationToken cancellationToken);    Task AddAsync(Order order, CancellationToken cancellationToken);}
// Registrationbuilder.Services.AddScoped<IOrderRepository, SqlOrderRepository>();

异步/等待模式

正确使用异步

csharp
// Good: Async all the way, with CancellationTokenpublic async Task<OrderSummary> GetOrderSummaryAsync(    Guid orderId,    CancellationToken cancellationToken){    var order = await _repository.FindByIdAsync(orderId, cancellationToken)        ?? throw new NotFoundException($"Order {orderId} not found");
    var customer = await _customerService.GetAsync(order.CustomerId, cancellationToken);
    return new OrderSummary(order, customer);}
// Bad: Blocking on asyncpublic OrderSummary GetOrderSummary(Guid orderId){    var order = _repository.FindByIdAsync(orderId, CancellationToken.None).Result; // Deadlock risk    return new OrderSummary(order);}

并行异步操作

csharp
// Good: Concurrent independent operationspublic async Task<DashboardData> LoadDashboardAsync(CancellationToken cancellationToken){    var ordersTask = _orderService.GetRecentAsync(cancellationToken);    var metricsTask = _metricsService.GetCurrentAsync(cancellationToken);    var alertsTask = _alertService.GetActiveAsync(cancellationToken);
    await Task.WhenAll(ordersTask, metricsTask, alertsTask);
    return new DashboardData(        Orders: await ordersTask,        Metrics: await metricsTask,        Alerts: await alertsTask);}

选项模式

将配置节绑定到强类型对象。

csharp
public sealed class SmtpOptions{    public const string SectionName = "Smtp";
    public required string Host { get; init; }    public required int Port { get; init; }    public required string Username { get; init; }    public bool UseSsl { get; init; } = true;}
// Registrationbuilder.Services.Configure<SmtpOptions>(    builder.Configuration.GetSection(SmtpOptions.SectionName));
// Usage via injectionpublic class EmailService(IOptions<SmtpOptions> options){    private readonly SmtpOptions _smtp = options.Value;}

结果模式

对预期失败返回显式成功/失败,而非抛出异常。

csharp
public sealed record Result<T>{    public bool IsSuccess { get; }    public T? Value { get; }    public string? Error { get; }
    private Result(T value) { IsSuccess = true; Value = value; }    private Result(string error) { IsSuccess = false; Error = error; }
    public static Result<T> Success(T value) => new(value);    public static Result<T> Failure(string error) => new(error);}
// Usagepublic async Task<Result<Order>> PlaceOrderAsync(CreateOrderRequest request){    if (request.Items.Count == 0)        return Result<Order>.Failure("Order must contain at least one item");
    var order = Order.Create(request);    await _repository.AddAsync(order, CancellationToken.None);    return Result<Order>.Success(order);}

使用 EF Core 的仓储模式

csharp
public sealed class SqlOrderRepository : IOrderRepository{    private readonly AppDbContext _db;
    public SqlOrderRepository(AppDbContext db) => _db = db;
    public async Task<Order?> FindByIdAsync(Guid id, CancellationToken cancellationToken)    {        return await _db.Orders            .Include(o => o.Items)            .AsNoTracking()            .FirstOrDefaultAsync(o => o.Id == id, cancellationToken);    }
    public async Task<IReadOnlyList<Order>> FindByCustomerAsync(        string customerId,        CancellationToken cancellationToken)    {        return await _db.Orders            .Where(o => o.CustomerId == customerId)            .OrderByDescending(o => o.CreatedAt)            .AsNoTracking()            .ToListAsync(cancellationToken);    }
    public async Task AddAsync(Order order, CancellationToken cancellationToken)    {        _db.Orders.Add(order);        await _db.SaveChangesAsync(cancellationToken);    }}

中间件与管道

csharp
// Custom middlewarepublic sealed class RequestTimingMiddleware{    private readonly RequestDelegate _next;    private readonly ILogger<RequestTimingMiddleware> _logger;
    public RequestTimingMiddleware(RequestDelegate next, ILogger<RequestTimingMiddleware> logger)    {        _next = next;        _logger = logger;    }
    public async Task InvokeAsync(HttpContext context)    {        var stopwatch = Stopwatch.StartNew();        try        {            await _next(context);        }        finally        {            stopwatch.Stop();            _logger.LogInformation(                "Request {Method} {Path} completed in {ElapsedMs}ms with status {StatusCode}",                context.Request.Method,                context.Request.Path,                stopwatch.ElapsedMilliseconds,                context.Response.StatusCode);        }    }}

最小 API 模式

csharp
// Organized with route groupsvar orders = app.MapGroup("/api/orders")    .RequireAuthorization()    .WithTags("Orders");
orders.MapGet("/{id:guid}", async (    Guid id,    IOrderRepository repository,    CancellationToken cancellationToken) =>{    var order = await repository.FindByIdAsync(id, cancellationToken);    return order is not null        ? TypedResults.Ok(order)        : TypedResults.NotFound();});
orders.MapPost("/", async (    CreateOrderRequest request,    IOrderService service,    CancellationToken cancellationToken) =>{    var result = await service.PlaceOrderAsync(request, cancellationToken);    return result.IsSuccess        ? TypedResults.Created($"/api/orders/{result.Value!.Id}", result.Value)        : TypedResults.BadRequest(result.Error);});

守卫子句

csharp
// Good: Early returns with clear validationpublic async Task<ProcessResult> ProcessPaymentAsync(    PaymentRequest request,    CancellationToken cancellationToken){    ArgumentNullException.ThrowIfNull(request);
    if (request.Amount <= 0)        throw new ArgumentOutOfRangeException(nameof(request.Amount), "Amount must be positive");
    if (string.IsNullOrWhiteSpace(request.Currency))        throw new ArgumentException("Currency is required", nameof(request.Currency));
    // Happy path continues here without nesting    var gateway = _gatewayFactory.Create(request.Currency);    return await gateway.ChargeAsync(request, cancellationToken);}

应避免的反模式

反模式修复方案
async void 方法返回 Task(事件处理程序除外)
.Result 或 .Wait()使用 await
catch (Exception) { }处理或带上下文重新抛出
构造函数中的 new Service()使用构造函数注入
public 字段使用带适当访问器的属性
业务逻辑中的 dynamic使用泛型或显式类型
可变的 static 状态使用依赖注入作用域或 ConcurrentDictionary
循环中的 string.Format使用 StringBuilder 或内插字符串处理程序

Source and attribution

Source:affaan-m/ECCindocs/zh-CN/skills/dotnet-patternsat commitef648e0

License: No license

Content belongs to its original authors. SourceWeft indexes it from a public repository.

Report or request removal