
.NET Conventions
PopularC# coding standards for the Exceptionless codebase. Naming conventions, async patterns, structured logging, nullable reference types, and formatting rules. Keywords: C# style, naming conventions, _camelCase, PascalCase, async suffix, CancellationToken, nullable annotations, structured logging, ExceptionlessState
|
.NET Conventions
Style & Formatting
- Follow
.editorconfigrules strictly - Run
dotnet formatbefore 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/#endregiondirectives — they hide code and discourage refactoring
Async Patterns
- Suffix: Always use
Asyncsuffix 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).
You Might Also Like
Related Skills

verify
Use when you want to validate changes before committing, or when you need to check all React contribution requirements.
facebook
test
Use when you need to run tests for React core. Supports source, www, stable, and experimental channels.
facebook
feature-flags
Use when feature flag tests fail, flags need updating, understanding @gate pragmas, debugging channel-specific test failures, or adding new flags to React.
facebook
extract-errors
Use when adding new error messages to React, or seeing "unknown error code" warnings.
facebook