|
| 1 | +# .NET 8+ Best Practices Instructions |
| 2 | + |
| 3 | +# GitHub Copilot Instructions for .NET 8+ |
| 4 | + |
| 5 | +Generate code following these modern .NET 8+ best practices: |
| 6 | + |
| 7 | +## C# Language and Syntax |
| 8 | +- Always maintain semicolons after namespace declarations |
| 9 | +- Use C# 12+ features including primary constructors, required properties, and file-scoped namespaces |
| 10 | +- Implement pattern matching for complex conditionals |
| 11 | +- Use nullable reference types with proper null checks everywhere |
| 12 | +- Use records for DTOs and immutable data structures |
| 13 | +- Use init-only properties for immutable objects |
| 14 | +- Use collection expressions where appropriate |
| 15 | + |
| 16 | +## Logging |
| 17 | +- Include ILogger in all classes using constructor injection |
| 18 | +- Follow proper log level usage: |
| 19 | + - LogDebug: Detailed information for debugging |
| 20 | + - LogInformation: General operational information and successful operations |
| 21 | + - LogWarning: Non-critical issues or unexpected behavior |
| 22 | + - LogError: Errors and exceptions that prevent normal operation |
| 23 | + - LogCritical: Critical failures requiring immediate attention |
| 24 | +- Use structured logging with proper context: |
| 25 | + - Always include correlation IDs in logs |
| 26 | + - Use semantic logging with named parameters (e.g., `_logger.LogInformation("User {UserId} performed {Action}", userId, action)`) |
| 27 | +- Add logging for all major operations and exception paths |
| 28 | + |
| 29 | +## API Endpoints and Controllers |
| 30 | +- Use minimal APIs for simple CRUD operations |
| 31 | +- Use controller-based APIs for complex business logic |
| 32 | +- Apply consistent HTTP status codes based on operation outcomes |
| 33 | +- Use proper route patterns with versioning support |
| 34 | +- Always use attribute routing |
| 35 | +- Return ActionResult<T> from controller methods |
| 36 | +- Apply authorization and custom attributes to each endpoint |
| 37 | + |
| 38 | +## Custom Attributes |
| 39 | +- Apply ActivityLog attribute to all action methods to capture user activities: |
| 40 | + - Format: `[ActivityLog("Descriptive action message")]` |
| 41 | +- Apply AuthorizePermission attribute for fine-grained authorization: |
| 42 | + - Format: `[AuthorizePermission("Resource.Operation")]` |
| 43 | +- Place custom attributes before standard HTTP method attributes |
| 44 | + |
| 45 | +## Input Validation |
| 46 | +- Use FluentValidation for complex validation rules |
| 47 | +- Implement proper model validation with consistent error responses |
| 48 | +- Return ProblemDetails for validation errors |
| 49 | +- Validate request models early in the pipeline |
| 50 | + |
| 51 | +## Exception Handling |
| 52 | +- Implement global exception handling middleware |
| 53 | +- Use custom exception types for different business scenarios |
| 54 | +- Return appropriate status codes based on exception types |
| 55 | +- Include correlation IDs in error responses |
| 56 | +- Never expose sensitive information in error messages |
| 57 | + |
| 58 | +## Authentication & Authorization |
| 59 | +- Use JWT authentication with proper token validation |
| 60 | +- Implement role and policy-based authorization |
| 61 | +- Apply AuthorizePermission attributes consistently |
| 62 | +- Use scoped authorization policies |
| 63 | +- Implement proper refresh token mechanisms |
| 64 | + |
| 65 | +## Performance |
| 66 | +- Use asynchronous methods throughout the application |
| 67 | +- Implement appropriate caching strategies |
| 68 | +- Use output caching for GET endpoints |
| 69 | +- Implement rate limiting and request throttling |
| 70 | +- Use minimal serialization options |
| 71 | + |
| 72 | +## API Documentation |
| 73 | +- Include comprehensive Swagger/OpenAPI documentation |
| 74 | +- Add proper XML comments for all public APIs |
| 75 | +- Provide examples for request/response models |
| 76 | +- Document all possible response codes |
| 77 | + |
| 78 | +## Sample Controller Implementation |
| 79 | + |
| 80 | +``` |
| 81 | +using Contact.Api.Core.Attributes; |
| 82 | +using Contact.Application.Interfaces; |
| 83 | +using Contact.Application.UseCases.ContactPerson; |
| 84 | +using Microsoft.AspNetCore.Authorization; |
| 85 | +using Microsoft.AspNetCore.Mvc; |
| 86 | +
|
| 87 | +namespace Contact.Api.Controllers; |
| 88 | +
|
| 89 | +[Route("api/[controller]")] |
| 90 | +[ApiController] |
| 91 | +[Authorize] |
| 92 | +public class ContactPersonController(IContactPersonService contactPersonService) : ControllerBase |
| 93 | +{ |
| 94 | + [HttpPost] |
| 95 | + [ActivityLog("Creating new Contact")] |
| 96 | + [AuthorizePermission("Contacts.Create")] |
| 97 | + public async Task<IActionResult> Add(CreateContactPerson createContactPerson) |
| 98 | + { |
| 99 | + var createdContactPerson = await contactPersonService.Add(createContactPerson); |
| 100 | + return CreatedAtAction(nameof(GetById), new { id = createdContactPerson.Id }, createdContactPerson); |
| 101 | + } |
| 102 | +
|
| 103 | + [HttpPut("{id}")] |
| 104 | + [ActivityLog("Updating Contact")] |
| 105 | + [AuthorizePermission("Contacts.Update")] |
| 106 | + public async Task<IActionResult> Update(Guid id, UpdateContactPerson updateContactPerson) |
| 107 | + { |
| 108 | + var contactPerson = await contactPersonService.FindByID(id); |
| 109 | + if (contactPerson is null) return NotFound(); |
| 110 | + |
| 111 | + var updatedContactPerson = await contactPersonService.Update(updateContactPerson); |
| 112 | + return Ok(updatedContactPerson); |
| 113 | + } |
| 114 | +
|
| 115 | + [HttpDelete("{id}")] |
| 116 | + [ActivityLog("Deleting Contact")] |
| 117 | + [AuthorizePermission("Contacts.Delete")] |
| 118 | + public async Task<IActionResult> Delete(Guid id) |
| 119 | + { |
| 120 | + var deleted = await contactPersonService.Delete(id); |
| 121 | + return deleted ? NoContent() : NotFound(); |
| 122 | + } |
| 123 | +
|
| 124 | + [HttpGet("{id}")] |
| 125 | + [ActivityLog("Reading Contact By id")] |
| 126 | + [AuthorizePermission("Contacts.Read")] |
| 127 | + public async Task<IActionResult> GetById(Guid id) |
| 128 | + { |
| 129 | + var contactPerson = await contactPersonService.FindByID(id); |
| 130 | + return contactPerson is null ? NotFound() : Ok(contactPerson); |
| 131 | + } |
| 132 | +
|
| 133 | + [HttpGet] |
| 134 | + [ActivityLog("Reading All Contacts")] |
| 135 | + [AuthorizePermission("Contacts.Read")] |
| 136 | + public async Task<IActionResult> GetAll() |
| 137 | + { |
| 138 | + var contactPersons = await contactPersonService.FindAll(); |
| 139 | + return Ok(contactPersons); |
| 140 | + } |
| 141 | +} |
| 142 | +
|
| 143 | +``` |
0 commit comments