| name | dotnet-guidelines |
| description | C# coding guidelines and best practices. MUST follow these rules. Use when reviewing/writing C# code or dotnet tasks. |
IMPORTANT Server Guidelines
- ALWAYS Use Primary Constructors for classes.
- ALWAYS Prefer
recordtypes for immutable data structures. - NEVER mix multiple classes or DTOs in a single file, even if they are small. Filename ALWAYS matches the class or DTO name, for example
UserDto.csfor aUserDtoclass. - Private fields should NOT be prefixed with
_(e.g.,repositorynot_repository). - Add (.) to the end of all server-side log messages.
- Use
string?overstringfor nullable strings. - Use
varfor local variables when type is obvious. - Don't use Regions in code files.
- Use Collection initializers for collections: instead of
new List<string>(), usenew() { "item1", "item2" }, and instead oflist.ToArray()use[.. list]. - Use Anonymous function static: instead of
list.Select(x => new Dto(x.SameProperty)), uselist.Select(static x => new Dto(x.SameProperty)). - ALWAYS use
sealedaccessor for classes or records if not intended for inheritance. - ALWAYS use
internalaccessor for internal classes or records. - NEVER inject or create
HttpClientandIHttpClientFactory, instead use theIRestClientinterface. - NEVER register services directly in
IServiceCollection. Instead, use theITransientService,IScopedService, orISingletonServiceinterfaces to register services with the appropriate lifetimes. - ALWAYS use
ILoggerwith Structured logging to log, no string interpolation and no other logging methods. - ALWAYS use cancellation tokens for asynchronous methods.
- ALWAYS use
IMemoryCorefor time-based in-memory caching, NEVER useIMemoryCache. - ALWAYS use Data Transfer Objects (DTO) for API communication, validated with attributes.
- ALWAYS use
xas a parameter name in lambdas and anonymous functions. - NEVER use try-catch blocks solely to log and rethrow exceptions.
- If any backend changes are made, run
dotnet buildin the root and ensure no build errors for the entire solution.
Code Organization Principles
Avoid Unnecessary Wrapper Methods
Rule: Do NOT create wrapper methods for specific cases that simply call another method with fixed parameters. Call the base method directly instead.
Example - DON'T:
// ❌ Don't create this wrapper method for a single use case:
public async Task<int> CreateAgentHistoryClearedMessageAsync(int sessionId)
{
var request = new CreateMessageRequest(
Type: MessageType.System,
Content: "Agent history has been cleared",
Status: MessageStatus.Done
);
return await CreateMessageAsync(sessionId, request);
}
Example - DO:
// ✅ Instead, call CreateMessageAsync directly where needed:
await messageService.CreateMessageAsync(
sessionId,
new CreateMessageRequest(
MessageType.System,
"Agent history has been cleared",
MessageStatus.Done
)
);
When to create a helper method:
- When there's complex logic beyond just parameter mapping
- When the same combination is used in multiple places (3+ times)
- When the method provides meaningful abstraction or validation