Skip to content

Commit 7ce52b1

Browse files
committed
Add SerilogTest
1 parent 22c8720 commit 7ce52b1

11 files changed

Lines changed: 417 additions & 0 deletions

File tree

.cursor/rules/dotnet-rules.mdc

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
---
2+
description:
3+
globs: *.cs
4+
alwaysApply: false
5+
---
6+
7+
# .NET Development Rules
8+
9+
You are a senior .NET backend developer and an expert in C#, ASP.NET Core, and Entity Framework Core.
10+
11+
## Code Style and Structure
12+
13+
- Write concise, idiomatic C# code with accurate examples.
14+
- Follow .NET and ASP.NET Core conventions and best practices.
15+
- Use object-oriented and functional programming patterns as appropriate.
16+
- Prefer LINQ and lambda expressions for collection operations.
17+
- Use descriptive variable and method names (e.g., 'IsUserSignedIn', 'CalculateTotal').
18+
- Structure files according to .NET conventions (Controllers, Models, Services, etc.).
19+
20+
## Naming Conventions
21+
22+
- Use PascalCase for class names, method names, and public members.
23+
- Use camelCase for local variables and private fields.
24+
- Use UPPERCASE for constants.
25+
- Prefix interface names with "I" (e.g., 'IUserService').
26+
27+
## C# and .NET Usage
28+
29+
- Use C# 10+ features when appropriate (e.g., record types, pattern matching, null-coalescing assignment).
30+
- Leverage built-in ASP.NET Core features and middleware.
31+
- Use Entity Framework Core effectively for database operations.
32+
33+
## Syntax and Formatting
34+
35+
- Follow the C# Coding Conventions (https://docs.microsoft.com/en-us/dotnet/csharp/fundamentals/coding-style/coding-conventions)
36+
- Use C#'s expressive syntax (e.g., null-conditional operators, string interpolation)
37+
- Use 'var' for implicit typing when the type is obvious.
38+
39+
## Error Handling and Validation
40+
41+
- Use exceptions for exceptional cases, not for control flow.
42+
- Implement proper error logging using built-in .NET logging or a third-party logger.
43+
- Use Data Annotations or Fluent Validation for model validation.
44+
- Implement global exception handling middleware.
45+
- Return appropriate HTTP status codes and consistent error responses.
46+
47+
## API Design
48+
49+
- Follow RESTful API design principles.
50+
- Use attribute routing in controllers.
51+
- Implement versioning for your API.
52+
- Use action filters for cross-cutting concerns.
53+
54+
## Performance Optimization
55+
56+
- Use asynchronous programming with async/await for I/O-bound operations.
57+
- Implement caching strategies using IMemoryCache or distributed caching.
58+
- Use efficient LINQ queries and avoid N+1 query problems.
59+
- Implement pagination for large data sets.
60+
61+
## Key Conventions
62+
63+
- Use Dependency Injection for loose coupling and testability.
64+
- Implement repository pattern or use Entity Framework Core directly, depending on the complexity.
65+
- Use AutoMapper for object-to-object mapping if needed.
66+
- Implement background tasks using IHostedService or BackgroundService.
67+
68+
## Testing
69+
70+
- Write unit tests using xUnit, NUnit, or MSTest.
71+
- Use Moq or NSubstitute for mocking dependencies.
72+
- Implement integration tests for API endpoints.
73+
74+
## Security
75+
76+
- Use Authentication and Authorization middleware.
77+
- Implement JWT authentication for stateless API authentication.
78+
- Use HTTPS and enforce SSL.
79+
- Implement proper CORS policies.
80+
81+
Follow the official Microsoft documentation and ASP.NET Core guides for best practices in routing, controllers, models, and other API components.

src/Example.CSharp.sln

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -267,6 +267,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ExceptionHelperTest", "11\E
267267
EndProject
268268
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NRedisStackTest", "plugins\NRedisStackTest\NRedisStackTest.csproj", "{805752FF-E206-49FA-8959-AE79DDA1DBEA}"
269269
EndProject
270+
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SerilogTest", "plugins\SerilogTest\SerilogTest.csproj", "{5C904F2C-2E56-49B7-9BC0-9F61A435B6BF}"
271+
EndProject
270272
Global
271273
GlobalSection(SolutionConfigurationPlatforms) = preSolution
272274
Debug|Any CPU = Debug|Any CPU
@@ -649,6 +651,10 @@ Global
649651
{805752FF-E206-49FA-8959-AE79DDA1DBEA}.Debug|Any CPU.Build.0 = Debug|Any CPU
650652
{805752FF-E206-49FA-8959-AE79DDA1DBEA}.Release|Any CPU.ActiveCfg = Release|Any CPU
651653
{805752FF-E206-49FA-8959-AE79DDA1DBEA}.Release|Any CPU.Build.0 = Release|Any CPU
654+
{5C904F2C-2E56-49B7-9BC0-9F61A435B6BF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
655+
{5C904F2C-2E56-49B7-9BC0-9F61A435B6BF}.Debug|Any CPU.Build.0 = Debug|Any CPU
656+
{5C904F2C-2E56-49B7-9BC0-9F61A435B6BF}.Release|Any CPU.ActiveCfg = Release|Any CPU
657+
{5C904F2C-2E56-49B7-9BC0-9F61A435B6BF}.Release|Any CPU.Build.0 = Release|Any CPU
652658
EndGlobalSection
653659
GlobalSection(SolutionProperties) = preSolution
654660
HideSolutionNode = FALSE
@@ -760,6 +766,7 @@ Global
760766
{8AA2BF21-0152-408B-AB29-0C55D2F94395} = {719AF6DF-48D1-4C7B-A8E9-493482D109DE}
761767
{BB9D0A15-F03D-4000-BAD3-CFF79A168E3C} = {3EAB4251-F872-4E23-AFEE-C55FEC5856DD}
762768
{805752FF-E206-49FA-8959-AE79DDA1DBEA} = {630114C3-1AE7-468E-8ADD-1CDE3E9EEF26}
769+
{5C904F2C-2E56-49B7-9BC0-9F61A435B6BF} = {630114C3-1AE7-468E-8ADD-1CDE3E9EEF26}
763770
EndGlobalSection
764771
GlobalSection(ExtensibilityGlobals) = postSolution
765772
SolutionGuid = {CBE0CD6C-2E47-4D9F-B072-C138AA4A6D5A}
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
using Microsoft.AspNetCore.Mvc;
2+
3+
namespace SerilogTest.Controllers;
4+
5+
[ApiController]
6+
[Route("[controller]")]
7+
public class WeatherForecastController : ControllerBase
8+
{
9+
10+
#region Constants & Statics
11+
12+
private static readonly string[] Summaries = new[]
13+
{
14+
"Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching"
15+
};
16+
17+
#endregion
18+
19+
private readonly ILogger<WeatherForecastController> _logger;
20+
21+
public WeatherForecastController(ILogger<WeatherForecastController> logger)
22+
{
23+
_logger = logger;
24+
}
25+
26+
#region Methods
27+
28+
[HttpGet(Name = "GetWeatherForecast")]
29+
public IEnumerable<WeatherForecast> Get()
30+
{
31+
_logger.LogInformation("GetWeatherForecast called");
32+
33+
var arr = Enumerable.Range(1, 5)
34+
.Select(
35+
index =>
36+
new WeatherForecast
37+
{
38+
Date = DateOnly.FromDateTime(DateTime.Now.AddDays(index)),
39+
TemperatureC = Random.Shared.Next(-20, 55),
40+
Summary = Summaries[Random.Shared.Next(Summaries.Length)]
41+
})
42+
.ToArray();
43+
44+
_logger.LogWarning("result is {@Arr}", arr);
45+
46+
return arr;
47+
}
48+
49+
#endregion
50+
51+
}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
namespace SerilogTest;
2+
3+
public class MyException : Exception
4+
{
5+
public MyException()
6+
{
7+
}
8+
9+
public MyException(string message) : base(message)
10+
{
11+
}
12+
13+
public MyException(string message, Exception inner) : base(message, inner)
14+
{
15+
}
16+
}

src/plugins/SerilogTest/Program.cs

Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
using Serilog;
2+
using Serilog.Exceptions;
3+
using Serilog.Exceptions.Core;
4+
using Serilog.Exceptions.EntityFrameworkCore.Destructurers;
5+
6+
namespace SerilogTest;
7+
8+
internal static class Program
9+
{
10+
11+
#region Constants & Statics
12+
13+
private static async Task Main(string[] args)
14+
{
15+
var configuration = new ConfigurationBuilder()
16+
.SetBasePath(Directory.GetCurrentDirectory())
17+
.AddJsonFile("appsettings.json")
18+
.AddJsonFile(
19+
$"appsettings.{Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT") ?? "Production"}.json",
20+
true)
21+
.Build();
22+
23+
Log.Logger = new LoggerConfiguration()
24+
.ReadFrom
25+
.Configuration(configuration) // BUG: invalid
26+
.Enrich
27+
.WithExceptionDetails(
28+
new DestructuringOptionsBuilder().WithDefaultDestructurers()
29+
.WithDestructurers([new DbUpdateExceptionDestructurer()]))
30+
.CreateBootstrapLogger();
31+
32+
try
33+
{
34+
Log.Information("Starting web application");
35+
36+
#pragma warning disable CA2201 // Do not raise reserved exception types
37+
Log.Error(new MyException("Host error.", new Exception("inner exception")), "test exception");
38+
#pragma warning restore CA2201 // Do not raise reserved exception types
39+
40+
var builder = WebApplication.CreateBuilder(args);
41+
42+
// Add services to the container.
43+
_ = builder.Host
44+
.UseSerilog(
45+
(builderContext, serviceProvider, options) =>
46+
{
47+
_ = options.ReadFrom.Configuration(builderContext.Configuration).ReadFrom
48+
.Services(serviceProvider);
49+
});
50+
51+
_ = builder.Services.AddControllers();
52+
// Learn more about configuring OpenAPI at https://aka.ms/aspnet/openapi
53+
_ = builder.Services.AddOpenApi();
54+
55+
var app = builder.Build();
56+
57+
// Configure the HTTP request pipeline.
58+
if (app.Environment.IsDevelopment())
59+
{
60+
_ = app.MapOpenApi();
61+
}
62+
63+
if (!app.Environment.IsDevelopment())
64+
{
65+
_ = app.UseHsts();
66+
}
67+
68+
_ = app.UseSerilogRequestLogging(
69+
(options) =>
70+
{
71+
// Attach additional properties to the request completion event
72+
options.EnrichDiagnosticContext = (diagnosticContext, httpContext) =>
73+
{
74+
diagnosticContext.Set("RequestHost", httpContext.Request.Host);
75+
diagnosticContext.Set("RequestScheme", httpContext.Request.Scheme);
76+
};
77+
});
78+
79+
_ = app.UseHttpsRedirection();
80+
81+
_ = app.UseAuthorization();
82+
83+
_ = app.MapControllers();
84+
85+
await app.RunAsync();
86+
87+
Log.Information("Stoped web application");
88+
}
89+
catch (Exception ex)
90+
{
91+
Log.Fatal(ex, "Application terminated unexpectedly");
92+
}
93+
finally
94+
{
95+
await Log.CloseAndFlushAsync();
96+
}
97+
}
98+
99+
#endregion
100+
101+
}
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
{
2+
"$schema": "https://json.schemastore.org/launchsettings.json",
3+
"profiles": {
4+
"http": {
5+
"commandName": "Project",
6+
"dotnetRunMessages": true,
7+
"launchBrowser": false,
8+
"applicationUrl": "http://localhost:5267",
9+
"environmentVariables": {
10+
"ASPNETCORE_ENVIRONMENT": "Development"
11+
}
12+
},
13+
"https": {
14+
"commandName": "Project",
15+
"dotnetRunMessages": true,
16+
"launchBrowser": false,
17+
"applicationUrl": "https://localhost:7119;http://localhost:5267",
18+
"environmentVariables": {
19+
"ASPNETCORE_ENVIRONMENT": "Development"
20+
}
21+
}
22+
}
23+
}
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
<Project Sdk="Microsoft.NET.Sdk.Web">
2+
3+
<PropertyGroup>
4+
<TargetFramework>net9.0</TargetFramework>
5+
<Nullable>enable</Nullable>
6+
<ImplicitUsings>enable</ImplicitUsings>
7+
</PropertyGroup>
8+
9+
<ItemGroup>
10+
<PackageReference Include="Microsoft.AspNetCore.OpenApi" Version="9.0.4" />
11+
<PackageReference Include="Serilog.AspNetCore" Version="9.0.0" />
12+
<PackageReference Include="Serilog.Enrichers.ClientInfo" Version="2.1.2" />
13+
<PackageReference Include="Serilog.Enrichers.Environment" Version="3.0.1" />
14+
<PackageReference Include="Serilog.Enrichers.Process" Version="3.0.0" />
15+
<PackageReference Include="Serilog.Enrichers.Thread" Version="4.0.0" />
16+
<PackageReference Include="Serilog.Exceptions" Version="8.4.0" />
17+
<PackageReference Include="Serilog.Exceptions.EntityFrameworkCore" Version="8.4.0" />
18+
<PackageReference Include="Serilog.Sinks.Async" Version="2.1.0" />
19+
</ItemGroup>
20+
21+
</Project>
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
@SerilogTest_HostAddress = http://localhost:5267
2+
3+
GET {{SerilogTest_HostAddress}}/weatherforecast/
4+
Accept: application/json
5+
6+
###
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
namespace SerilogTest;
2+
3+
public class WeatherForecast
4+
{
5+
public DateOnly Date { get; set; }
6+
7+
public int TemperatureC { get; set; }
8+
9+
public int TemperatureF => 32 + (int)(TemperatureC / 0.5556);
10+
11+
public string? Summary { get; set; }
12+
}
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
{
2+
"Serilog": {
3+
"MinimumLevel": {
4+
"Default": "Debug",
5+
"Override": {
6+
"Microsoft": "Debug",
7+
"System": "Information"
8+
}
9+
},
10+
"WriteTo": [
11+
{
12+
"Name": "Console",
13+
"Args": {
14+
"outputTemplate": "{Timestamp:HH:mm:ss} [{Level:u3}] {Message:lj}{NewLine}{Exception}"
15+
}
16+
}
17+
]
18+
},
19+
"Logging": {
20+
"LogLevel": {
21+
"Default": "Information",
22+
"Microsoft.AspNetCore": "Warning"
23+
}
24+
}
25+
}

0 commit comments

Comments
 (0)