IMPORTANT Server Guidelines
- ALWAYS Use Primary Constructors for classes.
- ALWAYS Prefer
record types 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.cs for a UserDto class.
- Private fields should NOT be prefixed with
_ (e.g., repository not _repository).
- Add (.) to the end of all server-side log messages.
- Use
string? over string for nullable strings.
- Use
var for local variables when type is obvious.
- Don't use Regions in code files.
- Use Collection initializers for collections: instead of
new List<string>(), use new() { "item1", "item2" }, and instead of list.ToArray() use [.. list].
- Use Anonymous function static: instead of
list.Select(x => new Dto(x.SameProperty)), use list.Select(static x => new Dto(x.SameProperty)).
- ALWAYS use
sealed accessor for classes or records if not intended for inheritance.
- ALWAYS use
internal accessor for internal classes or records.
- NEVER inject or create
HttpClient and IHttpClientFactory, instead use the IRestClient interface.
- NEVER register services directly in
IServiceCollection. Instead, use the ITransientService, IScopedService, or ISingletonService interfaces to register services with the appropriate lifetimes.
- ALWAYS use
ILogger with Structured logging to log, no string interpolation and no other logging methods.
- ALWAYS use cancellation tokens for asynchronous methods.
- ALWAYS use
IMemoryCore for time-based in-memory caching, NEVER use IMemoryCache.
- ALWAYS use Data Transfer Objects (DTO) for API communication, validated with attributes.
- ALWAYS use
x as a parameter name in lambdas and anonymous functions.
- If any backend changes are made, run
dotnet build in 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
1---2name: dotnet-guidelines3description: dotnet coding guidelines and best practices. MUST follow these rules. Use when reviewing or writing .NET code or tasks.4---5
6# IMPORTANT Server Guidelines
7
8- ALWAYS Use Primary Constructors for classes.
9- ALWAYS Prefer `record` types for immutable data structures.
10- 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.cs` for a `UserDto` class.
11- Private fields should NOT be prefixed with `_` (e.g., `repository` not `_repository`).
12- Add (.) to the end of all server-side log messages.
13- Use `string?` over `string` for nullable strings.
14- Use `var` for local variables when type is obvious.
15- Don't use Regions in code files.
16- Use Collection initializers for collections: instead of `new List<string>()`, use `new() { "item1", "item2" }`, and instead of `list.ToArray()` use `[.. list]`.
17- Use Anonymous function static: instead of `list.Select(x => new Dto(x.SameProperty))`, use `list.Select(static x => new Dto(x.SameProperty))`.
18- ALWAYS use `sealed` accessor for classes or records if not intended for inheritance.
19- ALWAYS use `internal` accessor for internal classes or records.
20- NEVER inject or create `HttpClient` and `IHttpClientFactory`, instead use the `IRestClient` interface.
21- NEVER register services directly in `IServiceCollection`. Instead, use the `ITransientService`, `IScopedService`, or `ISingletonService` interfaces to register services with the appropriate lifetimes.
22- ALWAYS use `ILogger` with Structured logging to log, no string interpolation and no other logging methods.
23- ALWAYS use cancellation tokens for asynchronous methods.
24- ALWAYS use `IMemoryCore` for time-based in-memory caching, NEVER use `IMemoryCache`.
25- ALWAYS use Data Transfer Objects (DTO) for API communication, validated with attributes.
26- ALWAYS use `x` as a parameter name in lambdas and anonymous functions.
27- If any backend changes are made, run `dotnet build` in the root and ensure no build errors for the entire solution.
28
29## Code Organization Principles
30
31### Avoid Unnecessary Wrapper Methods
32
33**Rule**: Do NOT create wrapper methods for specific cases that simply call another method with fixed parameters. Call the base method directly instead.
34
35**Example - DON'T**:
36
37```csharp
38// ❌ Don't create this wrapper method for a single use case:
39public async Task<int> CreateAgentHistoryClearedMessageAsync(int sessionId)
40{
41 var request = new CreateMessageRequest(
42 Type: MessageType.System,
43 Content: "Agent history has been cleared",
44 Status: MessageStatus.Done
45 );
46 return await CreateMessageAsync(sessionId, request);
47}
48```
49
50**Example - DO**:
51
52```csharp
53// ✅ Instead, call CreateMessageAsync directly where needed:
54await messageService.CreateMessageAsync(
55 sessionId,
56 new CreateMessageRequest(
57 MessageType.System,
58 "Agent history has been cleared",
59 MessageStatus.Done
60 )
61);
62```
63
64**When to create a helper method**:
65
66- When there's complex logic beyond just parameter mapping
67- When the same combination is used in multiple places (3+ times)
68- When the method provides meaningful abstraction or validation