.agents/skills/dotnet-quality/SKILL.md
.NET code quality with Roslyn analyzers, StyleCop, and dotnet format. Covers linting, formatting, and best practices for C#/.NET. USE WHEN: user works with "C#", ".NET", "ASP.NET Core", asks about "Roslyn analyzers", "StyleCop", "dotnet format", ".NET linting", "C# best practices" DO NOT USE FOR: SonarQube - use `sonarqube` skill, testing - use .NET test skills, security - use `dotnet-security` skill
npx skillsauth add d-subrahmanyam/deno-fresh-microservices dotnet-qualityInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
sonarqube skilldotnet-security skillDeep Knowledge: Use
mcp__documentation__fetch_docswith technology:dotnetfor comprehensive documentation.
| Tool | Focus | Installation | |------|-------|--------------| | dotnet format | Formatting + analyzers | Built-in | | Roslyn Analyzers | Code analysis | NuGet | | StyleCop.Analyzers | Style rules | NuGet | | Roslynator | Refactoring | NuGet | | SonarAnalyzer.CSharp | SonarQube rules | NuGet |
<!-- Directory.Build.props (solution-wide) -->
<Project>
<PropertyGroup>
<AnalysisLevel>latest-all</AnalysisLevel>
<EnforceCodeStyleInBuild>true</EnforceCodeStyleInBuild>
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.CodeAnalysis.NetAnalyzers" Version="8.0.0">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets>
</PackageReference>
<PackageReference Include="StyleCop.Analyzers" Version="1.2.0-beta.556">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets>
</PackageReference>
<PackageReference Include="Roslynator.Analyzers" Version="4.10.0">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets>
</PackageReference>
</ItemGroup>
</Project>
# .editorconfig
root = true
[*]
indent_style = space
indent_size = 4
end_of_line = lf
charset = utf-8
trim_trailing_whitespace = true
insert_final_newline = true
[*.cs]
# Organize usings
dotnet_sort_system_directives_first = true
dotnet_separate_import_directive_groups = false
# this. qualification
dotnet_style_qualification_for_field = false:warning
dotnet_style_qualification_for_property = false:warning
dotnet_style_qualification_for_method = false:warning
dotnet_style_qualification_for_event = false:warning
# Language keywords vs BCL types
dotnet_style_predefined_type_for_locals_parameters_members = true:warning
dotnet_style_predefined_type_for_member_access = true:warning
# var preferences
csharp_style_var_for_built_in_types = true:suggestion
csharp_style_var_when_type_is_apparent = true:suggestion
csharp_style_var_elsewhere = true:suggestion
# Expression-bodied members
csharp_style_expression_bodied_methods = when_on_single_line:suggestion
csharp_style_expression_bodied_constructors = false:suggestion
csharp_style_expression_bodied_properties = true:suggestion
csharp_style_expression_bodied_accessors = true:suggestion
csharp_style_expression_bodied_lambdas = true:suggestion
# Pattern matching
csharp_style_pattern_matching_over_is_with_cast_check = true:warning
csharp_style_pattern_matching_over_as_with_null_check = true:warning
# Null checking
csharp_style_throw_expression = true:suggestion
csharp_style_conditional_delegate_call = true:warning
dotnet_style_coalesce_expression = true:warning
dotnet_style_null_propagation = true:warning
# Code style
csharp_prefer_braces = true:warning
csharp_prefer_simple_using_statement = true:suggestion
csharp_style_prefer_switch_expression = true:suggestion
# Naming conventions
dotnet_naming_rule.interface_should_be_begins_with_i.severity = warning
dotnet_naming_rule.interface_should_be_begins_with_i.symbols = interface
dotnet_naming_rule.interface_should_be_begins_with_i.style = begins_with_i
dotnet_naming_symbols.interface.applicable_kinds = interface
dotnet_naming_symbols.interface.applicable_accessibilities = public, internal, private, protected
dotnet_naming_style.begins_with_i.required_prefix = I
dotnet_naming_style.begins_with_i.capitalization = pascal_case
dotnet_naming_rule.private_field_should_be_camel_case_with_underscore.severity = warning
dotnet_naming_rule.private_field_should_be_camel_case_with_underscore.symbols = private_field
dotnet_naming_rule.private_field_should_be_camel_case_with_underscore.style = camel_case_underscore
dotnet_naming_symbols.private_field.applicable_kinds = field
dotnet_naming_symbols.private_field.applicable_accessibilities = private
dotnet_naming_style.camel_case_underscore.required_prefix = _
dotnet_naming_style.camel_case_underscore.capitalization = camel_case
# Analyzer severity
dotnet_diagnostic.CA1062.severity = warning # Validate arguments
dotnet_diagnostic.CA1307.severity = warning # Specify StringComparison
dotnet_diagnostic.CA1310.severity = warning # Specify StringComparison for correctness
dotnet_diagnostic.CA2007.severity = none # ConfigureAwait (not needed in ASP.NET Core)
dotnet_diagnostic.IDE0058.severity = none # Expression value never used
# StyleCop
dotnet_diagnostic.SA1101.severity = none # Prefix local calls with this
dotnet_diagnostic.SA1309.severity = none # Field names must not begin with underscore
dotnet_diagnostic.SA1600.severity = none # Elements should be documented
dotnet_diagnostic.SA1633.severity = none # File should have header
# Check formatting
dotnet format --verify-no-changes
# Fix formatting
dotnet format
# Specific project
dotnet format ./src/MyProject
# Analyzers only
dotnet format analyzers
# Style only
dotnet format style
# Whitespace only
dotnet format whitespace
# With severity
dotnet format --severity warn
// BAD - No null check
public void Process(string input)
{
Console.WriteLine(input.Length); // CA1062
}
// GOOD - Null check
public void Process(string input)
{
ArgumentNullException.ThrowIfNull(input);
Console.WriteLine(input.Length);
}
// GOOD - Nullable reference type
public void Process(string? input)
{
if (input is null) return;
Console.WriteLine(input.Length);
}
// BAD - Culture-dependent
if (name.Equals("admin")) // CA1307
// GOOD - Explicit comparison
if (name.Equals("admin", StringComparison.OrdinalIgnoreCase))
// GOOD - For user-facing
if (name.Equals(otherName, StringComparison.CurrentCultureIgnoreCase))
// BAD - Not disposed
public void Process()
{
var stream = new FileStream("file.txt", FileMode.Open); // CA2000
// forgot to dispose
}
// GOOD - Using statement
public void Process()
{
using var stream = new FileStream("file.txt", FileMode.Open);
// automatically disposed
}
// Before
List<string> items = new List<string>();
// After
List<string> items = new();
// Or with var
var items = new List<string>();
// BAD - Does too much
public class OrderProcessor
{
public void CreateOrder() { ... }
public void SendEmail() { ... }
public void GeneratePdf() { ... }
public void CalculateTax() { ... }
}
// GOOD - Single responsibility
public class OrderService
{
private readonly IEmailService _emailService;
private readonly IPdfGenerator _pdfGenerator;
private readonly ITaxCalculator _taxCalculator;
public OrderService(
IEmailService emailService,
IPdfGenerator pdfGenerator,
ITaxCalculator taxCalculator)
{
_emailService = emailService;
_pdfGenerator = pdfGenerator;
_taxCalculator = taxCalculator;
}
public async Task<Order> CreateOrderAsync(OrderRequest request)
{
var order = BuildOrder(request);
order.Tax = _taxCalculator.Calculate(order);
return order;
}
}
// BAD - Blocking async
public void Process()
{
var result = GetDataAsync().Result; // Deadlock risk!
}
// BAD - Async void (except event handlers)
public async void ProcessAsync() // Can't be awaited, exceptions lost
{
await DoWorkAsync();
}
// GOOD - Async all the way
public async Task ProcessAsync()
{
var result = await GetDataAsync();
}
// GOOD - When truly fire-and-forget
public void StartBackgroundWork()
{
_ = Task.Run(async () =>
{
try { await DoWorkAsync(); }
catch (Exception ex) { _logger.LogError(ex, "Background work failed"); }
});
}
// Enable in .csproj
// <Nullable>enable</Nullable>
// BAD - Ignoring nullability
public string GetName(User user)
{
return user.Name; // Warning if Name is string?
}
// GOOD - Handle null
public string GetName(User user)
{
return user.Name ?? "Unknown";
}
// GOOD - Return nullable
public string? GetName(User? user)
{
return user?.Name;
}
// BAD - Mutable class with boilerplate
public class UserDto
{
public int Id { get; set; }
public string Name { get; set; }
public string Email { get; set; }
// Equals, GetHashCode, ToString...
}
// GOOD - Immutable record
public record UserDto(int Id, string Name, string Email);
// With validation
public record UserDto
{
public int Id { get; init; }
public string Name { get; init; }
public string Email { get; init; }
public UserDto(int id, string name, string email)
{
ArgumentException.ThrowIfNullOrWhiteSpace(name);
ArgumentException.ThrowIfNullOrWhiteSpace(email);
Id = id;
Name = name;
Email = email;
}
}
// BAD - Type checking with cast
if (shape is Circle)
{
var circle = (Circle)shape;
return circle.Radius * circle.Radius * Math.PI;
}
// GOOD - Pattern matching
if (shape is Circle circle)
{
return circle.Radius * circle.Radius * Math.PI;
}
// GOOD - Switch expression
return shape switch
{
Circle c => c.Radius * c.Radius * Math.PI,
Rectangle r => r.Width * r.Height,
_ => throw new ArgumentException("Unknown shape")
};
repos:
- repo: local
hooks:
- id: dotnet-format
name: dotnet format
entry: dotnet format --verify-no-changes
language: system
types: [c#]
pass_filenames: false
- id: dotnet-build
name: dotnet build
entry: dotnet build --no-restore -warnaserror
language: system
types: [c#]
pass_filenames: false
| Metric | Target | Tool | |--------|--------|------| | Cyclomatic Complexity | < 10 | Roslynator | | Method Lines | < 50 | StyleCop | | Parameters | < 5 | CA1026 | | Test Coverage | > 80% | coverlet | | Maintainability Index | > 60 | VS Metrics |
name: Quality
on: [push, pull_request]
jobs:
quality:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Setup .NET
uses: actions/setup-dotnet@v4
with:
dotnet-version: '8.0.x'
- name: Restore
run: dotnet restore
- name: Format check
run: dotnet format --verify-no-changes
- name: Build
run: dotnet build --no-restore -warnaserror
- name: Test
run: dotnet test --no-build --collect:"XPlat Code Coverage"
- name: Upload coverage
uses: codecov/codecov-action@v4
// .vscode/settings.json
{
"omnisharp.enableEditorConfigSupport": true,
"omnisharp.enableRoslynAnalyzers": true,
"[csharp]": {
"editor.defaultFormatter": "ms-dotnettools.csharp",
"editor.formatOnSave": true
}
}
| Anti-Pattern | Why It's Bad | Correct Approach |
|--------------|--------------|------------------|
| .Result / .Wait() | Deadlock risk | Use async/await |
| async void | Exceptions lost | Use async Task |
| Ignoring CA warnings | Real issues hidden | Fix or suppress with reason |
| No nullable reference types | NullReferenceException | Enable <Nullable>enable</Nullable> |
| #pragma warning disable | Hides issues | Fix or suppress specifically |
| Mutable DTOs | Unexpected changes | Use records or init-only |
| Issue | Likely Cause | Solution |
|-------|--------------|----------|
| Analyzer not running | Package not installed | Add to .csproj |
| Too many warnings | First-time enable | Suppress and fix incrementally |
| Format changes on build | Different settings | Commit .editorconfig |
| Nullable warnings everywhere | Legacy code | Enable gradually per project |
| StyleCop conflicts | Different conventions | Configure in .editorconfig |
development
Guidelines for building high-performance APIs with Fastify and TypeScript, covering validation, Prisma integration, and testing best practices
development
FastAPI modern Python web framework. Covers routing, Pydantic models, dependency injection, and async support. Use when building Python APIs. USE WHEN: user mentions "fastapi", "pydantic", "async python api", "python rest api", asks about "dependency injection python", "python openapi", "python swagger", "async endpoints", "python api validation", "fastapi middleware" DO NOT USE FOR: Django apps - use `django` instead, Flask apps - use `flask` instead, synchronous Python APIs without type hints, GraphQL-only APIs
tools
FastAPI integration testing specialist. Covers synchronous TestClient, async httpx AsyncClient, dependency injection overrides, auth testing (JWT, OAuth2, API keys), WebSocket testing, file uploads, background tasks, middleware testing, and HTTP mocking with respx, responses, and pytest-httpserver. USE WHEN: user mentions "FastAPI test", "TestClient", "httpx async test", "dependency override test", "respx mock", asks about testing FastAPI endpoints, authentication in tests, or HTTP client mocking. DO NOT USE FOR: Django - use `pytest-django`; pytest internals - use `pytest`; Container infrastructure - use `testcontainers-python`
development
Expert in FastAPI Python development with best practices for APIs and async operations