Add JobExceptionHandlingBehavior and improve mappings
Introduced `JobExceptionHandlingBehavior` to handle exceptions, log errors, and rethrow them during MediatR pipeline execution. Updated `DependencyInjection.cs` to register the new behavior and added a `recClientApiUrl` parameter for API configuration. Enhanced `ProfileMappingProfile.cs` and `GetProfileQuery.cs` with XML documentation for better readability. Improved case-insensitive filtering in `GetProfileQuery` with conditional compilation for .NET version compatibility. Modified `CreateProfileHistoryCommand.cs` to use a non-nullable `AddedWho` property. Added missing `using` directive in `GetProfileQuery.cs` for compatibility. These changes improve code quality, maintainability, and functionality.
This commit is contained in:
@@ -10,6 +10,9 @@ namespace ECMJobRunner.Application.Common.Mapping
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public class ProfileMappingProfile : Profile
|
public class ProfileMappingProfile : Profile
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Configures AutoMapper mappings for <see cref="ECMJobRunner.Domain.Entities.CfgProfile"/> and <see cref="ECMJobRunner.Domain.Entities.ProfileSqlJob"/> entities
|
||||||
|
/// </summary>
|
||||||
public ProfileMappingProfile()
|
public ProfileMappingProfile()
|
||||||
{
|
{
|
||||||
// CfgProfile -> CfgProfileDto
|
// CfgProfile -> CfgProfileDto
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ namespace ECMJobRunner.Application
|
|||||||
/// Registers MediatR, pipeline behaviors, and AutoMapper
|
/// Registers MediatR, pipeline behaviors, and AutoMapper
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="services">The service collection</param>
|
/// <param name="services">The service collection</param>
|
||||||
|
/// <param name="recClientApiUrl">The base URL for the ReC client API</param>
|
||||||
/// <returns>The service collection for chaining</returns>
|
/// <returns>The service collection for chaining</returns>
|
||||||
public static IServiceCollection AddJobRunnerServices(this IServiceCollection services, string recClientApiUrl)
|
public static IServiceCollection AddJobRunnerServices(this IServiceCollection services, string recClientApiUrl)
|
||||||
{
|
{
|
||||||
@@ -37,6 +38,7 @@ namespace ECMJobRunner.Application
|
|||||||
|
|
||||||
// Register pipeline behaviors in execution order
|
// Register pipeline behaviors in execution order
|
||||||
// Order matters: MainQuery -> CheckQuery -> ReCRequest
|
// Order matters: MainQuery -> CheckQuery -> ReCRequest
|
||||||
|
services.AddTransient(typeof(IPipelineBehavior<,>), typeof(JobExceptionHandlingBehavior<,>));
|
||||||
services.AddTransient(typeof(IPipelineBehavior<,>), typeof(MainQueryExecutionBehavior<,>));
|
services.AddTransient(typeof(IPipelineBehavior<,>), typeof(MainQueryExecutionBehavior<,>));
|
||||||
services.AddTransient(typeof(IPipelineBehavior<,>), typeof(CheckQueryExecutionBehavior<,>));
|
services.AddTransient(typeof(IPipelineBehavior<,>), typeof(CheckQueryExecutionBehavior<,>));
|
||||||
services.AddTransient(typeof(IPipelineBehavior<,>), typeof(ReCRequestExecutionBehavior<,>));
|
services.AddTransient(typeof(IPipelineBehavior<,>), typeof(ReCRequestExecutionBehavior<,>));
|
||||||
|
|||||||
@@ -37,7 +37,7 @@ namespace ECMJobRunner.Application.ProfileHistories.Commands
|
|||||||
/// Created by (max 50 chars)
|
/// Created by (max 50 chars)
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[JsonIgnore]
|
[JsonIgnore]
|
||||||
public string AddedWho { get; set; } = "ECMJobRunner";
|
public string AddedWho { get; set; } = null!;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|||||||
@@ -0,0 +1,47 @@
|
|||||||
|
using ECMJobRunner.Application.Common.Exceptions;
|
||||||
|
using ECMJobRunner.Application.ProfileHistories.Commands;
|
||||||
|
using ECMJobRunner.Domain.ValueObjects;
|
||||||
|
using MediatR;
|
||||||
|
using System.Threading;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace ECMJobRunner.Application.Profiles.Commands.Behaviors;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Pipeline behavior that catches <see cref="ECMJobRunner.Application.Common.Exceptions.JobException"/> exceptions,
|
||||||
|
/// persists a profile history error record and re-throws the exception
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="TRequest">The type of the MediatR request</typeparam>
|
||||||
|
/// <typeparam name="TResponse">The type of the MediatR response</typeparam>
|
||||||
|
/// <param name="Sender">MediatR sender used to dispatch the <see cref="ECMJobRunner.Application.ProfileHistories.Commands.CreateProfileHistoryCommand"/></param>
|
||||||
|
public class JobExceptionHandlingBehavior<TRequest, TResponse>(ISender Sender) : IPipelineBehavior<TRequest, TResponse> where TRequest : notnull
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Handles the pipeline behavior
|
||||||
|
/// Executes main query if request is TriggeringDEXJobCommand
|
||||||
|
/// </summary>
|
||||||
|
#if NET48
|
||||||
|
public async Task<TResponse> Handle(TRequest request, CancellationToken cancellationToken, RequestHandlerDelegate<TResponse> next)
|
||||||
|
#else
|
||||||
|
public async Task<TResponse> Handle(TRequest request, RequestHandlerDelegate<TResponse> next, CancellationToken cancellationToken)
|
||||||
|
#endif
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
return await next();
|
||||||
|
}
|
||||||
|
catch (JobException ex)
|
||||||
|
{
|
||||||
|
var cmd = new CreateProfileHistoryCommand()
|
||||||
|
{
|
||||||
|
ProfileId = ex.ProfileId,
|
||||||
|
Result = ResultType.Ok,
|
||||||
|
ResultText = ex.Message,
|
||||||
|
AddedWho = "ECMJobRunner"
|
||||||
|
};
|
||||||
|
await Sender.Send(cmd, cancellationToken);
|
||||||
|
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -2,6 +2,7 @@ using AutoMapper;
|
|||||||
using ECMJobRunner.Application.Common.Dtos;
|
using ECMJobRunner.Application.Common.Dtos;
|
||||||
using ECMJobRunner.Domain.Interfaces;
|
using ECMJobRunner.Domain.Interfaces;
|
||||||
using MediatR;
|
using MediatR;
|
||||||
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
@@ -58,12 +59,23 @@ namespace ECMJobRunner.Application.Profiles.Queries
|
|||||||
private readonly ICfgProfileRepository _profileRepository;
|
private readonly ICfgProfileRepository _profileRepository;
|
||||||
private readonly IMapper _mapper;
|
private readonly IMapper _mapper;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Constructor
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="profileRepository">Repository for profile data access</param>
|
||||||
|
/// <param name="mapper">AutoMapper instance for entity-to-DTO mapping</param>
|
||||||
public GetProfileQueryHandler(ICfgProfileRepository profileRepository, IMapper mapper)
|
public GetProfileQueryHandler(ICfgProfileRepository profileRepository, IMapper mapper)
|
||||||
{
|
{
|
||||||
_profileRepository = profileRepository;
|
_profileRepository = profileRepository;
|
||||||
_mapper = mapper;
|
_mapper = mapper;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Handles the <see cref="GetProfileQuery"/> by retrieving and mapping profiles
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="request">The query containing optional filter parameters</param>
|
||||||
|
/// <param name="cancellationToken">Cancellation token</param>
|
||||||
|
/// <returns>List of matched profiles mapped to <see cref="CfgProfileDto"/></returns>
|
||||||
public async Task<List<CfgProfileDto>> Handle(GetProfileQuery request, CancellationToken cancellationToken)
|
public async Task<List<CfgProfileDto>> Handle(GetProfileQuery request, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
IEnumerable<Domain.Entities.CfgProfile> profiles;
|
IEnumerable<Domain.Entities.CfgProfile> profiles;
|
||||||
@@ -112,8 +124,11 @@ namespace ECMJobRunner.Application.Profiles.Queries
|
|||||||
|
|
||||||
if (!string.IsNullOrWhiteSpace(request.ProfileName))
|
if (!string.IsNullOrWhiteSpace(request.ProfileName))
|
||||||
{
|
{
|
||||||
var searchName = request.ProfileName.ToLowerInvariant();
|
#if NET
|
||||||
profiles = profiles.Where(p => p.ProfileName.ToLowerInvariant().Contains(searchName));
|
profiles = profiles.Where(p => p.ProfileName.Contains(request.ProfileName, StringComparison.OrdinalIgnoreCase));
|
||||||
|
#else
|
||||||
|
profiles = profiles.Where(p => p.ProfileName.IndexOf(request.ProfileName!, StringComparison.OrdinalIgnoreCase) >= 0);
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user