| name | csharp-quality-developer |
| description | Enforce C# coding standards including StyleCop rules (SA1028, SA1518, SA1101, etc.), file formatting (CRLF, trailing whitespace), naming conventions (this. prefix, no underscore prefix), XML documentation, LoggerMessage patterns, and brace placement. Use when writing C# code, reviewing .cs files, fixing StyleCop violations, or ensuring code quality compliance. |
| metadata | {"category":"language-helpers"} |
Coding Standards Reminder for MSSQL MCP Server
Pre-Coding Checklist
Before writing ANY code, remember these critical rules to avoid rework:
1. File Format Requirements
- Line Endings: Use Windows CRLF (
\r\n) for all C# files
- End of File: ALWAYS end with exactly ONE newline character
- No Trailing Spaces: Never leave spaces at end of lines
- Indentation: Use 4 spaces (not tabs) for C# code
- Line Endings - Unix: Use Unix LF (
\n) for all .sh files
2. Common StyleCop Rules to Follow
Most Frequently Violated Rules (Fix These First!)
- SA1028: Code should not contain trailing whitespace
- SA1518: File must end with single newline character
- SA1101: Prefix local calls with
this.
- SA1116: Parameters should begin on line after declaration for multi-line
- SA1500: Braces for multi-line statements should not share line
- SA1505: Opening brace should not be followed by blank line
- SA1108: Block statements should not contain embedded comments
- SA1513: Closing brace should be followed by blank line
Documentation Rules
- SA1614: Element parameter documentation should have text (not just
<param name="x"></param>)
- SA1616: Element return value documentation should have text (not just
<returns></returns>)
- SA1629: Documentation text should end with a period
- SA1623: Property documentation should begin with "Gets or sets a value indicating whether" for bool properties
File Organization Rules
- SA1402: File may only contain a single type
- SA1649: File name should match first type name
- SA1201: Elements should be ordered correctly (enums should not follow classes)
- SA1210: Using directives should be ordered alphabetically by the namespaces
Other Important Rules
- IDE0055: Fix formatting (proper spacing and indentation)
- IDE0040: Interface members must have explicit accessibility modifiers (public)
- SA1633: File must have header (we suppress this, but include copyright)
- SA1309: Field names should not begin with underscore
- SA1111: Closing parenthesis should be on line of last parameter
- SA1009: Closing parenthesis should not be preceded by a space
- CA1812: Avoid uninstantiated internal classes (commonly suppressed for IoC/DI scenarios)
3. C# Code Style
using System;
using Microsoft.Extensions.Logging;
using MssqlMcp.Application;
namespace MssqlMcp.Application;
public sealed class ExampleClass
{
private readonly ILogger logger;
public ExampleClass(ILogger logger)
{
this.logger = logger;
}
public async Task<Result> DoWork()
{
this.logger.LogDebug("Working");
return new Result();
}
}
4. Common Mistakes to Avoid
Trailing Spaces and File Endings
public void Method()
{
var x = 1;
}
public class MyClass
{
}
public void Method()
{
var x = 1;
}
Field Names and 'this.' Usage
private readonly ILogger _logger;
logger.LogDebug("message");
private readonly ILogger logger;
this.logger.LogDebug("message");
Multi-line Parameters (SA1116)
builder.Services.AddOptions<MyOptions>(options =>
{
options.Value = "test";
});
builder.Services.AddOptions<MyOptions>(
options =>
{
options.Value = "test";
});
Documentation Issues
public string Process(string input) { }
public bool IsLoggingEnabled { get; set; }
public string Process(string input) { }
public bool IsLoggingEnabled { get; set; }
Embedded Comments (SA1108)
foreach (var item in items.TakeLast(3))
{
Console.WriteLine(item);
}
foreach (var item in items.TakeLast(3))
{
Console.WriteLine(item);
}
CA1812: Avoid Uninstantiated Internal Classes
internal class UnusedService
{
public void DoWork() { }
}
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1812:AvoidUninstantiatedInternalClasses", Justification = "Instantiated by IoC container")]
internal class InjectedService
{
public void DoWork() { }
}
[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage(
"Performance",
"CA1812:Avoid uninstantiated internal classes",
Justification = "Instantiated by dependency injection container",
Scope = "type",
Target = "~T:MyNamespace.InjectedService")]
<ItemGroup>
<InternalsVisibleTo Include="MyProject.Tests" />
<InternalsVisibleTo Include="MyProject.IntegrationTests" />
</ItemGroup>
[assembly: System.Runtime.CompilerServices.InternalsVisibleTo("MyProject.Tests")]
Using GlobalSuppressions.cs
using System.Diagnostics.CodeAnalysis;
[assembly: SuppressMessage(
"Performance",
"CA1812:Avoid uninstantiated internal classes",
Justification = "Classes are instantiated by dependency injection",
Scope = "namespaceanddescendants",
Target = "~N:MyProject.Services")]
[assembly: SuppressMessage(
"Performance",
"CA1812:Avoid uninstantiated internal classes",
Justification = "Instantiated by IoC container",
Scope = "type",
Target = "~T:MyProject.Services.EmailService")]
[assembly: SuppressMessage(
"Style",
"IDE0060:Remove unused parameter",
Justification = "Required by interface contract",
Scope = "member",
Target = "~M:MyProject.Services.MyService.Process(System.String)")]
Brace Placement (SA1500, SA1505, SA1513)
app.MapGet("/", () => {
return "Hello";
});
public void Method()
{
var x = 1;
}
if (condition)
{
DoSomething();
}
else
{
DoSomethingElse();
}
app.MapGet("/", () =>
{
return "Hello";
});
public void Method()
{
var x = 1;
}
if (condition)
{
DoSomething();
}
else
{
DoSomethingElse();
}
5. LoggerMessage Pattern - Use Decorator Pattern
Preferred Pattern for High-Performance Logging
[LoggerMessage(LogLevel.Information, "Entering {ClassName} for OrderNumber {orderNumber}, TaskNumber {TaskNumber}")]
private partial void LogEnter(string orderNumber, string taskNumber, string className = nameof(OrderService));
[LoggerMessage(LogLevel.Error, "Failed to process order {OrderId}")]
private partial void LogOrderError(int orderId, Exception exception);
this.LogEnter(order.Number, task.Number);
Important: LoggerMessage Analyzer Behavior
When to Use LoggerMessage
foreach (var item in items)
{
this.LogProcessingItem(item.Id);
}
this.logger.LogInformation("Application started");
6. Before Submitting Code
- Check for trailing spaces (especially after
; or })
- Verify file ends with single newline
- Ensure proper indentation (4 spaces)
- Verify all fields use
this. qualification
- Check that readonly fields are marked as such
- Fix all compilation errors before addressing LoggerMessage warnings
7. Project-Specific Rules
- Use
Microsoft.Identity.Client for OAuth
- Use
Microsoft.Data.SqlClient (not System.Data.SqlClient)
- Always validate operations against
RestrictToReadOnly flag
- Use structured logging with proper parameter names
- Handle async operations properly with ConfigureAwait(false) where appropriate
8. Testing Your Code
Before committing:
dotnet build
dotnet test
9. Internal Visibility Best Practices
When to Use InternalsVisibleTo
<ItemGroup>
<InternalsVisibleTo Include="$(AssemblyName).Tests" />
<InternalsVisibleTo Include="$(AssemblyName).IntegrationTests" />
<InternalsVisibleTo Include="MyProject.Tests, PublicKey=..." />
</ItemGroup>
Benefits of Proper Internal Visibility
- Security: Keep implementation details hidden from external consumers
- Testing: Allow unit tests to access internal classes without making them public
- API Surface: Reduce public API surface area for better maintainability
- Encapsulation: Better encapsulation of implementation details
namespace MyProject.Services;
internal sealed class EmailService : IEmailService
{
}
public interface IEmailService
{
Task SendAsync(string to, string subject, string body);
}
10. Git Configuration
Ensure git handles line endings correctly:
git config core.autocrlf true
git config --get core.autocrlf
Quick Reference
| Rule | Description | Example |
|---|
| SA1518 | End with newline | }\n not } |
| SA1028 | No trailing spaces | var x = 1; not var x = 1; |
| IDE0055 | Proper formatting | if (x) not if(x) |
| SA1101 | Use this. | this.field not field |
| SA1309 | No underscore | logger not _logger |
| CA1812 | Uninstantiated internal classes | Use GlobalSuppressions.cs for DI |
| - | InternalsVisibleTo | Allow tests to access internals |
| - | GlobalSuppressions.cs | Keep suppressions out of code |
Remember: It's better to write code correctly the first time than to fix it later!
Quality Error Fix Strategy
When encountering StyleCop or code quality errors during refactoring:
Iteration Strategy:
- Iteration 1: Manually fix issues based on specific error messages
- Iteration 2: Try another round of manual fixes if needed
- Iteration 3: Final attempt at manual fixes
- After 3 iterations: STOP and alert the user - Visual Studio UI is faster for complex fixes
Important Note:
Do NOT use dotnet format as it can:
- Remove necessary using statements (especially ai-plugins-and-skills-specific ones)
- Get confused by source-generated code (LoggerMessage patterns)
- Make incorrect "fixes" that break the build
Example Alert After 3 Iterations:
Multiple quality errors remaining. Please fix manually in Visual Studio:
- SA1101: Add 'this.' qualifiers
- SA1518: Missing newline at end of file
- CS0246: Missing using directives
- etc.
This prevents endless fix attempts and leverages Visual Studio's built-in quick fixes.