mcpField
.
.NET Conventions

.NET Conventions

github.com/exceptionless开发2.5k94 次安装今天更新
代码仓库

关于 .NET Conventions

面向维护 .NET 代码的开发者,汇总格式、命名、异步处理、日志、空值和验证约定。还提供资源释放与依赖注入的代码模式。

主要功能

  1. 01代码格式与命名规则
  2. 02异步方法与取消令牌
  3. 03结构化日志与作用域
  4. 04空值处理与资源释放
  5. 05依赖注入和输入验证

适用场景

  1. 01统一团队 .NET 代码风格
  2. 02规范异步调用链
  3. 03为日志补充上下文
  4. 04检查空值与资源管理

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/exceptionless/exceptionless --skill .net-conventions

安装后在 Claude Code 中输入/.net-conventions确认已加载。

详细介绍

.NET 约定

风格与格式

  • 严格遵循 .editorconfig 规则
  • 提交前运行 dotnet format
  • 尽量减少差异:只更改必要内容,保留现有格式和结构
  • 与周围代码的风格完全一致

命名约定

元素约定示例
私有字段_camelCase_organizationRepository
公有成员PascalCaseGetByIdAsync
局部变量camelCaseorganizationId
常量PascalCaseMaxRetryCount
类型参数T 前缀TModel

格式规则

  • 缩进:4 个空格,不使用制表符
  • 命名空间:文件范围(namespace Foo;)
  • Usings:置于命名空间外
  • 大括号:始终使用,即使代码块只有一行
  • 不使用 #region:切勿使用 #region/#endregion 指令——它们会隐藏代码并妨碍重构

异步模式

  • 后缀:异步方法始终使用 Async 后缀
  • CancellationToken:可用时沿调用链传递
  • ValueTask:对于经常同步完成的热路径,优先使用 ValueTask<T>
  • ConfigureAwait:ASP.NET Core 中不需要
// From src/Exceptionless.Core/Services/UsageService.cs
public async Task SavePendingUsageAsync()
{
    var utcNow = _timeProvider.GetUtcNow().UtcDateTime;
    await SavePendingOrganizationUsageAsync(utcNow);
    await SavePendingProjectUsageAsync(utcNow);
}

结构化日志记录

使用带有命名占位符的消息模板——值放在参数中,而不是使用字符串插值:

// ✅ Correct: Named placeholders for structured data
_logger.LogInformation("Saving org ({OrganizationId}-{OrganizationName}) event usage",
    organizationId, organization.Name);

_logger.LogError(ex, "Error retrieving event post payload: {Path}", path);

_logger.LogWarning("Unable to parse user agent {UserAgent}. Exception: {Message}",
    userAgent, ex.Message);

// ❌ Wrong: String interpolation loses structure
_logger.LogInformation($"Saving org {organizationId}");
使用 ExceptionlessState 的日志范围

使用范围为代码块中的所有日志条目添加上下文:

// From src/Exceptionless.Core/Jobs/EventPostsJob.cs
using var _ = _logger.BeginScope(new ExceptionlessState()
    .Organization(ep.OrganizationId)
    .Project(ep.ProjectId));

// All log entries in this scope automatically include org/project context
_logger.LogInformation("Processing event post");

添加标签和属性以提供更丰富的上下文:

using (_logger.BeginScope(new ExceptionlessState()
    .Organization(organization.Id)
    .Tag("Delete")
    .Tag("Bot")))
{
    _logger.LogInformation("Removing bot events");
}

可空引用类型

  • 始终遵循可空注解
  • 将可空警告视为错误
  • 对可空类型使用 ? 后缀
public async Task<User?> FindUserAsync(string? email)
{
    if (string.IsNullOrWhiteSpace(email))
        return null;

    return await _repository.FindByEmailAsync(email);
}

资源释放

// Prefer using declarations
using var stream = File.OpenRead(path);

// Async disposal
await using var connection = await CreateConnectionAsync();

构造函数注入

优先使用构造函数注入和只读字段:

// From src/Exceptionless.Core/Services/UsageService.cs
public class UsageService
{
    private readonly IOrganizationRepository _organizationRepository;
    private readonly ICacheClient _cache;
    private readonly TimeProvider _timeProvider;
    private readonly ILogger _logger;

    public UsageService(
        IOrganizationRepository organizationRepository,
        ICacheClient cache,
        TimeProvider timeProvider,
        ILoggerFactory loggerFactory)
    {
        _organizationRepository = organizationRepository;
        _cache = cache;
        _timeProvider = timeProvider;
        _logger = loggerFactory.CreateLogger<UsageService>();
    }
}

验证模式

输入验证

尽早验证,快速失败:

public async Task<ActionResult> ProcessAsync(string id)
{
    if (string.IsNullOrEmpty(id))
        return BadRequest("Id is required");

    var entity = await _repository.GetByIdAsync(id);
    if (entity is null)
        return NotFound();

    // Continue processing
}
领域验证

有关验证模式(领域模型使用 FluentValidation,API 请求使用 MiniValidator),请参阅 backend-architecture。

评论

支持 Markdown