affaan-m/ECC

dotnet-patterns

Use it for engineering tasks; the detail page covers purpose, installation, and practical steps.

69Collecting
See how to use itView GitHub source
npx skills add https://github.com/affaan-m/ECC --skill "docs/zh-CN/skills/dotnet-patterns"
Automated source guide

Source checked Jul 28, 2026·Refresh due Oct 26, 2026

Reorganized from the pinned upstream SKILL.md

Turn dotnet-patterns's source instructions into a guide you can follow

According to the pinned SKILL.md from affaan-m/ECC: 用于构建健壮、高性能且可维护应用程序的惯用 C 和 .NET 模式。

npx skills add https://github.com/affaan-m/ECC --skill "docs/zh-CN/skills/dotnet-patterns"
Check the pinned source

Best fit

  • Use it for engineering tasks; the detail page covers purpose, installation, and practical steps.

Bring this context

  • A concrete task that matches the documented purpose of dotnet-patterns.
  • The files, examples, or context the task depends on.
  • Your constraints, target environment, and definition of done.

Expected outputs

  • A result that follows the pinned dotnet-patterns instructions.
  • A concise record of assumptions, inputs used, and unresolved questions.
  • A final check against the source workflow and relevant permission signals.

Key source sections

Read dotnet-patterns through these 5 source sections

Sections are extracted automatically from the pinned SKILL.md and link back to the source.

01

何时激活

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

SKILL.md · 何时激活
编写新的 C 代码时审查 C 代码时重构现有 .NET 应用程序时
02

核心原则

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

SKILL.md · 核心原则
对数据模型使用记录和仅初始化属性。可变性应作为明确且有理由的选择。
03

1. 优先使用不可变性

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

SKILL.md · 1. 优先使用不可变性
对数据模型使用记录和仅初始化属性。可变性应作为明确且有理由的选择。
04

2. 显式优于隐式

Review the “2. 显式优于隐式” section in the pinned source before continuing.

SKILL.md · 2. 显式优于隐式
Review and apply the “2. 显式优于隐式” source section.
05

3. 依赖抽象

Review the “3. 依赖抽象” section in the pinned source before continuing.

SKILL.md · 3. 依赖抽象
Review and apply the “3. 依赖抽象” source section.

SkillSignal prompt templates

Provide the task, context, and acceptance criteria

These prompts were written by SkillSignal from the source structure; they are not upstream text.

Task-start prompt

Confirm source fit, inputs, and outputs before acting.

Use dotnet-patterns to help me with: [specific task]. Context: [files, data, or background]. Constraints: [environment, scope, and prohibited actions]. Before acting, check the pinned SKILL.md and explain which sections apply, what inputs are still missing, and what you will deliver.

Source-guided execution

Make the Agent explicitly follow the key extracted sections.

Apply the pinned dotnet-patterns source to [task]. Pay particular attention to these source sections: “何时激活”, “核心原则”, “1. 优先使用不可变性”, “2. 显式优于隐式”, “3. 依赖抽象”. Preserve the important decision at each step. Mark facts not covered by the source as “needs confirmation” instead of inventing them. Then verify the result against my acceptance criteria: [criteria].

Result-review prompt

Check omissions, permissions, and source drift before delivery.

Review the current dotnet-patterns result: (1) does it satisfy the original task; (2) were any applicable steps or limits in the pinned SKILL.md missed; (3) did it perform any unauthorized file, command, network, or data action; and (4) which conclusions remain unverified? List issues first, then fix only what the source or user authorization supports.

Output checklist

Verify each item before delivery

The task matches the purpose documented in the SKILL.md.

The source section “何时激活” has been checked.

The source section “核心原则” has been checked.

The source section “1. 优先使用不可变性” has been checked.

The source section “2. 显式优于隐式” has been checked.

Inputs, constraints, and acceptance criteria are explicit.

Unverified facts, compatibility, and outcome claims are clearly marked.

Any file, command, network, or data action has been reviewed.

Choose a different workflow

When another Skill is the better fit

FAQ

What does dotnet-patterns do?

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

How do I start using dotnet-patterns?

The catalog detected this source-specific install command: npx skills add https://github.com/affaan-m/ECC --skill "docs/zh-CN/skills/dotnet-patterns". Inspect the command and pinned source before running it.

Which Agent platforms does it declare?

No dedicated Agent platform is declared in the pinned source record.

Repository stars
234,327
Repository forks
35,711
Quality
69/100
Source repository last pushed

Quality breakdown

Based on traceable docs and repository signals; stars are not treated as quality.

69/100
Documentation26/30
Specificity11/25
Maintenance20/20
Trust signals12/25

Compare before choosing

Related Agent Skills and source variants

These links are selected from shared tasks, functions, stacks, platforms, and same-name variants. Compare the source owner, documentation, permissions, and maintenance signals.

View original Skill.mdThis page is parsed directly from the repository SKILL.md without editorial rewriting. Collected: Jul 28, 2026 · about 1 min

.NET 开发模式

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

何时激活

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

核心原则

1. 优先使用不可变性

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

// Good: Immutable value object
public sealed record Money(decimal Amount, string Currency);

// Good: Immutable DTO with init setters
public sealed class CreateOrderRequest
{
    public required string CustomerId { get; init; }
    public required IReadOnlyList<OrderItem> Items { get; init; }
}

// Bad: Mutable model with public setters
public class Order
{
    public string CustomerId { get; set; }
    public List<OrderItem> Items { get; set; }
}

2. 显式优于隐式

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

// Good: Explicit access modifiers and nullability
public 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. 依赖抽象

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

// Good: Interface-based dependency
public interface IOrderRepository
{
    Task<Order?> FindByIdAsync(Guid id, CancellationToken cancellationToken);
    Task<IReadOnlyList<Order>> FindByCustomerAsync(string customerId, CancellationToken cancellationToken);
    Task AddAsync(Order order, CancellationToken cancellationToken);
}

// Registration
builder.Services.AddScoped<IOrderRepository, SqlOrderRepository>();

异步/等待模式

正确使用异步

// Good: Async all the way, with CancellationToken
public 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 async
public OrderSummary GetOrderSummary(Guid orderId)
{
    var order = _repository.FindByIdAsync(orderId, CancellationToken.None).Result; // Deadlock risk
    return new OrderSummary(order);
}

并行异步操作

// Good: Concurrent independent operations
public 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);
}

选项模式

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

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;
}

// Registration
builder.Services.Configure<SmtpOptions>(
    builder.Configuration.GetSection(SmtpOptions.SectionName));

// Usage via injection
public class EmailService(IOptions<SmtpOptions> options)
{
    private readonly SmtpOptions _smtp = options.Value;
}

结果模式

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

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);
}

// Usage
public 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 的仓储模式

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);
    }
}

中间件与管道

// Custom middleware
public 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 模式

// Organized with route groups
var 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);
});

守卫子句

// Good: Early returns with clear validation
public 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 repo
affaan-m/ECC
Skill path
docs/zh-CN/skills/dotnet-patterns/SKILL.md
Commit SHA
4e973d3eaf92
Repository license
MIT
Data collected