with one click
csharp-workflow
C# and .NET project workflow guidelines. Activate when working with C# files (.cs), .csproj, .NET projects, or C#-specific tooling.
Menu
C# and .NET project workflow guidelines. Activate when working with C# files (.cs), .csproj, .NET projects, or C#-specific tooling.
| name | csharp-workflow |
| description | C# and .NET project workflow guidelines. Activate when working with C# files (.cs), .csproj, .NET projects, or C#-specific tooling. |
| location | user |
The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be interpreted as described in RFC 2119.
| Task | Tool | Command |
|---|---|---|
| Lint | Roslynator | dotnet roslynator analyze |
| Format | dotnet format | dotnet format |
| Build | dotnet | dotnet build |
| Test | dotnet | dotnet test |
| Publish | dotnet | dotnet publish |
| Watch | dotnet | dotnet watch run |
| NuGet restore | dotnet | dotnet restore |
| Add package | dotnet | dotnet add package <name> |
SHOULD use primary constructors for dependency injection and simple initialization:
public class UserService(IUserRepository repository, ILogger<UserService> logger)
{
public async Task<User?> GetByIdAsync(int id) => await repository.FindAsync(id);
}
SHOULD use collection expressions for collection initialization:
// Preferred
int[] numbers = [1, 2, 3, 4, 5];
List<string> names = ["Alice", "Bob", "Charlie"];
// Spread operator
int[] combined = [..firstArray, ..secondArray];
SHOULD use raw string literals for multi-line strings and strings containing quotes:
var json = """
{
"name": "Example",
"value": 42
}
""";
MUST use file-scoped namespaces to reduce nesting:
namespace MyApp.Services;
public class MyService { }
SHOULD use minimal APIs for microservices and simple endpoints:
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
app.MapGet("/api/users/{id}", async (int id, IUserService service) =>
await service.GetByIdAsync(id) is User user
? Results.Ok(user)
: Results.NotFound());
app.Run();
SHOULD design for AOT compatibility when targeting native deployment:
[JsonSerializable] for System.Text.Json[JsonSerializable(typeof(User))]
[JsonSerializable(typeof(List<User>))]
internal partial class AppJsonContext : JsonSerializerContext { }
MAY use keyed services for named dependency resolution:
builder.Services.AddKeyedSingleton<ICache, RedisCache>("redis");
builder.Services.AddKeyedSingleton<ICache, MemoryCache>("memory");
| Element | Convention | Example |
|---|---|---|
| Public members | PascalCase | GetUserAsync() |
| Private fields | _camelCase | _userRepository |
| Local variables | camelCase | userName |
| Constants | PascalCase | MaxRetryCount |
| Interfaces | IPascalCase | IUserService |
| Type parameters | TPascalCase | TEntity |
| Async methods | Async suffix | GetUserAsync() |
I prefix for interfacesAsync suffix for async methodsMUST enable nullable reference types in all projects:
<PropertyGroup>
<Nullable>enable</Nullable>
</PropertyGroup>
required keyword for required properties! (null-forgiving operator) except when absolutely necessarypublic class User
{
public required string Name { get; init; }
public string? Email { get; set; }
}
ConfigureAwait(false) in library codeCancellationToken for cancellable operationsValueTask for hot paths with frequent sync completionasync void except for event handlerspublic async Task<User?> GetUserAsync(int id, CancellationToken ct = default)
{
return await _repository.FindAsync(id, ct).ConfigureAwait(false);
}
Exceptionwhen clause for conditional catches// Registration
builder.Services.AddScoped<IUserService, UserService>();
builder.Services.AddSingleton<ICacheService, CacheService>();
builder.Services.AddTransient<IEmailService, EmailService>();
| Lifetime | Use Case |
|---|---|
| Singleton | Stateless services, caches |
| Scoped | Per-request services, DbContext |
| Transient | Lightweight, stateless operations |
MUST use records for DTOs and immutable data:
public record UserDto(int Id, string Name, string? Email);
public record CreateUserRequest(string Name, string Email);
public record ApiResponse<T>(T Data, bool Success, string? Error = null);
with expressions for immutable updatesSHOULD prefer method syntax over query syntax:
// Preferred
var adults = users
.Where(u => u.Age >= 18)
.OrderBy(u => u.Name)
.Select(u => new UserDto(u.Id, u.Name, u.Email));
// Avoid query syntax for simple queries
var adults = from u in users
where u.Age >= 18
select u;
Any() instead of Count() > 0FirstOrDefault() with predicate| Framework | Command |
|---|---|
| xUnit | dotnet test |
| NUnit | dotnet test |
| MSTest | dotnet test |
public class UserServiceTests
{
private readonly Mock<IUserRepository> _mockRepo = new();
private readonly UserService _sut;
public UserServiceTests()
{
_sut = new UserService(_mockRepo.Object);
}
[Fact]
public async Task GetByIdAsync_WhenUserExists_ReturnsUser()
{
// Arrange
var expected = new User { Id = 1, Name = "Test" };
_mockRepo.Setup(r => r.FindAsync(1, default))
.ReturnsAsync(expected);
// Act
var result = await _sut.GetByIdAsync(1);
// Assert
Assert.Equal(expected, result);
}
[Theory]
[InlineData(0)]
[InlineData(-1)]
public async Task GetByIdAsync_WhenIdInvalid_ThrowsArgumentException(int id)
{
await Assert.ThrowsAsync<ArgumentException>(() => _sut.GetByIdAsync(id));
}
}
[Theory] for parameterized testsMySolution/
├── src/
│ ├── MyApp.Api/
│ │ ├── Controllers/
│ │ ├── Endpoints/
│ │ └── Program.cs
│ ├── MyApp.Application/
│ │ ├── Services/
│ │ └── DTOs/
│ ├── MyApp.Domain/
│ │ ├── Entities/
│ │ └── Interfaces/
│ └── MyApp.Infrastructure/
│ ├── Data/
│ └── Services/
├── tests/
│ ├── MyApp.UnitTests/
│ └── MyApp.IntegrationTests/
├── Directory.Build.props
├── Directory.Packages.props
└── MySolution.sln
SHOULD use centralized build configuration:
<Project>
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
</PropertyGroup>
</Project>
SHOULD use Result pattern for expected failures:
public record Result<T>(T? Value, bool IsSuccess, string? Error = null)
{
public static Result<T> Success(T value) => new(value, true);
public static Result<T> Failure(string error) => new(default, false, error);
}
MUST use Options pattern for configuration:
public class DatabaseOptions
{
public const string SectionName = "Database";
public required string ConnectionString { get; init; }
public int MaxRetryCount { get; init; } = 3;
}
// Registration
builder.Services.Configure<DatabaseOptions>(
builder.Configuration.GetSection(DatabaseOptions.SectionName));
SHOULD enable code analysis:
<PropertyGroup>
<EnableNETAnalyzers>true</EnableNETAnalyzers>
<AnalysisLevel>latest-recommended</AnalysisLevel>
</PropertyGroup>
MUST include .editorconfig for consistent style across team.
Activate when user needs multi-URL scraping, browser automation pipelines, or efficient tool orchestration to reduce API round-trips and context usage.
Language-agnostic API design patterns covering REST and GraphQL, including resource naming, HTTP methods, status codes, versioning, pagination, filtering, authentication, error handling, and schema design. Activate when working with APIs, REST endpoints, GraphQL schemas, API documentation, OpenAPI/Swagger, JWT, OAuth2, endpoint design, API versioning, rate limiting, or GraphQL resolvers.
Git workflow and commit guidelines. Trigger keywords: git, commit, push, .git, version control. MUST be activated before ANY git commit, push, or version control operation. Includes security scanning for secrets (API keys, tokens, .env files), commit message formatting with HEREDOC, logical commit grouping (docs, test, feat, fix, refactor, chore, build, deps), push behavior rules, safety rules for hooks and force pushes, and CRITICAL safeguards for destructive operations (filter-branch, gc --prune, reset --hard). Activate when user requests committing changes, pushing code, creating commits, rewriting history, or performing any git operations including analyzing uncommitted changes.
Testing workflow patterns and quality standards. Activate when working with tests, test files, test directories, code quality tools, coverage reports, or testing tasks. Includes zero-warnings policy, targeted testing during development, mocking patterns, and best practices across languages.
Ansible automation workflow guidelines. Activate when working with Ansible playbooks, ansible-playbook, inventory files (.yml, .ini), or Ansible-specific patterns.
Claude Code AI-assisted development workflow. Activate when discussing Claude Code usage, AI-assisted coding, prompting strategies, or Claude Code-specific patterns.