.net conventions

📁 exceptionless/exceptionless 📅 Jan 1, 1970
25
总安装量
0
周安装量
#7966
全站排名
安装命令
npx skills add https://github.com/exceptionless/exceptionless --skill .NET Conventions

Skill 文档

.NET Conventions

Style & Formatting

  • Follow .editorconfig rules strictly
  • Run dotnet format before committing
  • Minimize diffs: Change only what’s necessary, preserve existing formatting and structure
  • Match surrounding code style exactly

Naming Conventions

Element Convention Example
Private fields _camelCase _organizationRepository
Public members PascalCase GetByIdAsync
Local variables camelCase organizationId
Constants PascalCase MaxRetryCount
Type parameters T prefix TModel

Formatting Rules

  • Indentation: 4 spaces, no tabs
  • Namespaces: File-scoped (namespace Foo;)
  • Usings: Outside namespace
  • Braces: Always use, even for single-line blocks
  • No #region: Never use #region/#endregion directives — they hide code and discourage refactoring

Async Patterns

  • Suffix: Always use Async suffix for async methods
  • CancellationToken: Pass through call chains when available
  • ValueTask: Prefer ValueTask<T> for hot paths that often complete synchronously
  • ConfigureAwait: Not required in 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);
}

Structured Logging

Use message templates with named placeholders — values go in the args, not string interpolation:

// ✅ 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}");

Log Scopes with ExceptionlessState

Use scopes to add context to all log entries within a block:

// 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");

Add tags and properties for richer context:

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

Nullable Reference Types

  • Honor nullable annotations throughout
  • Treat nullable warnings as errors
  • Use ? suffix for nullable types
public async Task<User?> FindUserAsync(string? email)
{
    if (string.IsNullOrWhiteSpace(email))
        return null;

    return await _repository.FindByEmailAsync(email);
}

Resource Disposal

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

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

Constructor Injection

Prefer constructor injection with readonly fields:

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

Validation Patterns

Input Validation

Validate early, fail fast:

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
}

Domain Validation

See backend-architecture for validation patterns (FluentValidation for domain models, MiniValidator for API requests).