Skip to content

Commit 17ea96e

Browse files
committed
Refactor exception handling; update dependencies
Exception handling pipeline refactored: generic parameter removed from handler classes, RequestExceptionHandlerState and ResultExceptionBehavior deleted, and registration updated. ValidationBehavior now throws ValidationException with failures. Updated several NuGet packages. Minor code and comment cleanups. PostgreSQL sink config in SerilogExtensions simplified.
1 parent 0b7f78f commit 17ea96e

14 files changed

Lines changed: 43 additions & 120 deletions

src/Application/Common/ExceptionHandlers/DbExceptionHandler.cs

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -6,24 +6,20 @@ namespace CleanArchitecture.Blazor.Application.Common.ExceptionHandlers;
66
/// Handles database update exceptions and converts them into Result or Result<T> responses.
77
/// Provides user-friendly error messages for various database constraint violations.
88
/// </summary>
9-
public sealed class DbExceptionHandler<TRequest, TResponse, TException>
9+
public sealed class DbExceptionHandler<TRequest, TResponse> : MessageExceptionHandler<TRequest, TResponse, DbUpdateException>
1010
where TRequest : IRequest<TResponse>
1111
where TResponse : IResult
12-
where TException : DbUpdateException
1312
{
1413
// Common constraint-name prefixes to strip
1514
private static readonly string[] ConstraintPrefixes = ["PK_", "FK_", "IX_", "UQ_", "UC_"];
16-
public ValueTask Handle(
15+
protected override ValueTask<ExceptionHandlingResult<TResponse>> Handle(
1716
TRequest request,
18-
TException exception,
19-
RequestExceptionHandlerState<TResponse> state,
17+
DbUpdateException exception,
2018
CancellationToken cancellationToken)
2119
{
2220
var errors = GetUserFriendlyErrors(exception);
2321
var failureResult = CreateFailureResult(errors);
24-
25-
state.SetHandled(failureResult);
26-
return ValueTask.CompletedTask;
22+
return Handled(failureResult);
2723
}
2824

2925
private TResponse CreateFailureResult(string[] errors)

src/Application/Common/ExceptionHandlers/FallbackExceptionHandler.cs

Lines changed: 6 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,16 @@
11
namespace CleanArchitecture.Blazor.Application.Common.ExceptionHandlers;
22

3-
public sealed class FallbackExceptionHandler<TRequest, TResponse, TException>
3+
public sealed class FallbackExceptionHandler<TRequest, TResponse> : MessageExceptionHandler<TRequest, TResponse>
44
where TRequest : IRequest<TResponse>
55
where TResponse : IResult
6-
where TException : Exception
76
{
8-
private readonly ILogger<FallbackExceptionHandler<TRequest, TResponse, TException>> _logger;
7+
private readonly ILogger<FallbackExceptionHandler<TRequest, TResponse>> _logger;
98

109
/// <summary>
11-
/// Initializes a new instance of the <see cref="GlobalExceptionHandler{TRequest, TResponse, TException}"/> class.
10+
/// Initializes a new instance of the <see cref="FallbackExceptionHandler{TRequest, TResponse}"/> class.
1211
/// </summary>
1312
/// <param name="logger">The logger.</param>
14-
public FallbackExceptionHandler(ILogger<FallbackExceptionHandler<TRequest, TResponse, TException>> logger)
13+
public FallbackExceptionHandler(ILogger<FallbackExceptionHandler<TRequest, TResponse>> logger)
1514
{
1615
_logger = logger;
1716
}
@@ -21,10 +20,9 @@ public FallbackExceptionHandler(ILogger<FallbackExceptionHandler<TRequest, TResp
2120
/// </summary>
2221
/// <param name="request">The request.</param>
2322
/// <param name="exception">The exception.</param>
24-
/// <param name="state">The request exception handler state.</param>
2523
/// <param name="cancellationToken">The cancellation token.</param>
2624
/// <returns>A task representing the asynchronous operation.</returns>
27-
public ValueTask Handle(TRequest request, TException exception, RequestExceptionHandlerState<TResponse> state,
25+
protected override ValueTask<ExceptionHandlingResult<TResponse>> Handle(TRequest request, Exception exception,
2826
CancellationToken cancellationToken)
2927
{
3028
TResponse failureResult;
@@ -54,8 +52,6 @@ public ValueTask Handle(TRequest request, TException exception, RequestException
5452

5553
failureResult = ResultFailureFactory.Create<TResponse>(errorMessages);
5654

57-
// Set the handled response
58-
state.SetHandled(failureResult!);
59-
return ValueTask.CompletedTask;
55+
return Handled(failureResult);
6056
}
6157
}

src/Application/Common/ExceptionHandlers/NotFoundExceptionHandler.cs

Lines changed: 13 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -4,18 +4,17 @@
44
/// Handles NotFoundException and converts them into Result or Result&lt;T&gt; responses.
55
/// Provides user-friendly error messages for entity not found scenarios.
66
/// </summary>
7-
public sealed class NotFoundExceptionHandler<TRequest, TResponse, TException>
7+
public sealed class NotFoundExceptionHandler<TRequest, TResponse> : MessageExceptionHandler<TRequest, TResponse, NotFoundException>
88
where TRequest : IRequest<TResponse>
99
where TResponse : IResult
10-
where TException : NotFoundException
1110
{
12-
private readonly ILogger<NotFoundExceptionHandler<TRequest, TResponse, TException>> _logger;
11+
private readonly ILogger<NotFoundExceptionHandler<TRequest, TResponse>> _logger;
1312

1413
/// <summary>
15-
/// Initializes a new instance of the <see cref="NotFoundExceptionHandler{TRequest, TResponse, TException}"/> class.
14+
/// Initializes a new instance of the <see cref="NotFoundExceptionHandler{TRequest, TResponse}"/> class.
1615
/// </summary>
1716
/// <param name="logger">The logger instance.</param>
18-
public NotFoundExceptionHandler(ILogger<NotFoundExceptionHandler<TRequest, TResponse, TException>> logger)
17+
public NotFoundExceptionHandler(ILogger<NotFoundExceptionHandler<TRequest, TResponse>> logger)
1918
{
2019
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
2120
}
@@ -25,23 +24,19 @@ public NotFoundExceptionHandler(ILogger<NotFoundExceptionHandler<TRequest, TResp
2524
/// </summary>
2625
/// <param name="request">The request that caused the exception.</param>
2726
/// <param name="exception">The NotFoundException to handle.</param>
28-
/// <param name="state">The request exception handler state.</param>
2927
/// <param name="cancellationToken">The cancellation token.</param>
3028
/// <returns>A task representing the asynchronous operation.</returns>
31-
public ValueTask Handle(TRequest request, TException exception, RequestExceptionHandlerState<TResponse> state,
29+
protected override ValueTask<ExceptionHandlingResult<TResponse>> Handle(TRequest request, NotFoundException exception,
3230
CancellationToken cancellationToken)
3331
{
34-
35-
var failureResult = CreateFailureResult(exception.Message);
36-
state.SetHandled(failureResult);
37-
38-
_logger.LogError(exception,
39-
"NotFoundException occurred for request {RequestType}: {ErrorMessage}",
40-
typeof(TRequest).Name,
41-
exception.Message);
42-
43-
44-
return ValueTask.CompletedTask;
32+
var failureResult = CreateFailureResult(exception.Message);
33+
34+
_logger.LogError(exception,
35+
"NotFoundException occurred for request {RequestType}: {ErrorMessage}",
36+
typeof(TRequest).Name,
37+
exception.Message);
38+
39+
return Handled(failureResult);
4540
}
4641

4742
/// <summary>

src/Application/Common/ExceptionHandlers/RequestExceptionHandlerState.cs

Lines changed: 0 additions & 13 deletions
This file was deleted.

src/Application/Common/ExceptionHandlers/ValidationExceptionHandler.cs

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,19 +2,17 @@
22
namespace CleanArchitecture.Blazor.Application.Common.ExceptionHandlers;
33

44
public sealed class
5-
ValidationExceptionHandler<TRequest, TResponse, TException>
5+
ValidationExceptionHandler<TRequest, TResponse> : MessageExceptionHandler<TRequest, TResponse, ValidationException>
66
where TRequest : IRequest<TResponse>
77
where TResponse : IResult
8-
where TException : ValidationException
98
{
109

11-
public ValueTask Handle(TRequest request, TException exception, RequestExceptionHandlerState<TResponse> state,
10+
protected override ValueTask<ExceptionHandlingResult<TResponse>> Handle(TRequest request, ValidationException exception,
1211
CancellationToken cancellationToken)
1312
{
1413
var errors = exception.Errors.Select(x => x.ErrorMessage).Distinct().ToArray();
1514
var failureResult = CreateFailureResult(errors);
16-
state.SetHandled(failureResult);
17-
return ValueTask.CompletedTask;
15+
return Handled(failureResult);
1816
}
1917

2018
private TResponse CreateFailureResult(string[] errors)

src/Application/DependencyInjection.cs

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,12 +22,15 @@ public static IServiceCollection AddApplication(this IServiceCollection services
2222
options.NotificationPublisherType = typeof(ChannelBasedNoWaitPublisher);
2323
options.ServiceLifetime = ServiceLifetime.Scoped;
2424
options.PipelineBehaviors = [
25+
typeof(FallbackExceptionHandler<,>),
26+
typeof(ValidationExceptionHandler<,>),
27+
typeof(NotFoundExceptionHandler<,>),
28+
typeof(DbExceptionHandler<,>),
2529
typeof(ValidationBehavior<,>),
26-
typeof(ResultExceptionBehavior<,>),
2730
typeof(PerformanceBehaviour<,>),
2831
typeof(FusionCacheBehaviour<,>),
2932
typeof(CacheInvalidationBehaviour<,>)
30-
];
33+
];
3134

3235
});
3336

src/Application/Pipeline/ResultExceptionBehavior.cs

Lines changed: 0 additions & 52 deletions
This file was deleted.

src/Application/Pipeline/ValidationBehavior.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
namespace CleanArchitecture.Blazor.Application.Pipeline;
1+
namespace CleanArchitecture.Blazor.Application.Pipeline;
22

33
public sealed class ValidationBehavior<TRequest, TResponse> : IPipelineBehavior<TRequest, TResponse>
44
where TRequest : class, IMessage
@@ -21,7 +21,7 @@ public async ValueTask<TResponse> Handle(
2121
var failures = await _validators.ValidateAsync(request, cancellationToken).ConfigureAwait(false);
2222

2323
if (failures.Any())
24-
throw new ValidationException(string.Join(", ", failures.Select(x => x.ErrorMessage)));
24+
throw new ValidationException(failures);
2525
}
2626

2727
return await next(request, cancellationToken).ConfigureAwait(false);

src/Infrastructure/Extensions/SerilogExtensions.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -181,8 +181,8 @@ private static void WriteToNpgsql(LoggerConfiguration serilogConfig, string? con
181181
LogEventLevel.Information,
182182
needAutoCreateTable: false,
183183
schemaName: "public",
184-
useCopy: false,
185-
failureCallback: e => Console.WriteLine($"Sink error: {e.Message}")
184+
useCopy: false
185+
186186
));
187187
}
188188

src/Infrastructure/Infrastructure.csproj

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,18 +9,18 @@
99
</PropertyGroup>
1010
<ItemGroup>
1111

12-
<PackageReference Include="Microsoft.Agents.AI.OpenAI" Version="1.19.0" />
12+
<PackageReference Include="Microsoft.Agents.AI.OpenAI" Version="1.20.0" />
1313
<PackageReference Include="OpenAI" Version="2.13.0" />
1414
<PackageReference Include="MaxMind.GeoIP2" Version="6.1.0" />
1515
<PackageReference Include="ClosedXML" Version="0.105.1" />
1616
<PackageReference Include="MailKit" Version="4.17.0" />
1717
<PackageReference Include="MimeKit" Version="4.17.0" />
1818
<PackageReference Include="Scriban" Version="7.2.6" />
19-
<PackageReference Include="ZiggyCreatures.FusionCache" Version="2.7.1" />
19+
<PackageReference Include="ZiggyCreatures.FusionCache" Version="2.7.2" />
2020
<PackageReference Include="Microsoft.AspNetCore.Authentication.Google" Version="10.0.11" />
2121
<PackageReference Include="Microsoft.AspNetCore.Authentication.MicrosoftAccount" Version="10.0.11" />
2222
<PackageReference Include="Minio" Version="7.0.0" />
23-
<PackageReference Include="QuestPDF" Version="2026.7.3" />
23+
<PackageReference Include="QuestPDF" Version="2026.8.0" />
2424
<PackageReference Include="Serilog.AspNetCore" Version="10.0.0" />
2525
<PackageReference Include="Serilog.Sinks.MSSqlServer" Version="10.0.0" />
2626
<PackageReference Include="Serilog.Sinks.Postgresql.Alternative" Version="4.3.0" />

0 commit comments

Comments
 (0)