Files
ECMJobRunner/ECMJobRunner.Application/Profiles/Queries/GetProfileQuery.cs
TekH 73db8fbd27 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.
2026-08-03 13:37:01 +02:00

140 lines
5.3 KiB
C#

using AutoMapper;
using ECMJobRunner.Application.Common.Dtos;
using ECMJobRunner.Domain.Interfaces;
using MediatR;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
namespace ECMJobRunner.Application.Profiles.Queries
{
/// <summary>
/// Query to retrieve profiles with flexible filtering options
/// All query options are nullable - when no filters are specified, returns all profiles
/// </summary>
public class GetProfileQuery : IRequest<List<CfgProfileDto>>
{
/// <summary>
/// Profile ID to retrieve (optional)
/// When specified, returns only the profile with this ID
/// </summary>
public long? Id { get; set; }
/// <summary>
/// Filter by active status (optional)
/// When null, returns both active and inactive profiles
/// When true, returns only active profiles
/// When false, returns only inactive profiles
/// </summary>
public bool? Active { get; set; }
/// <summary>
/// Filter by profile type (optional)
/// When specified, returns only profiles with this type
/// Type: 0 = ADSync; 1 = GraphQL; 2 = SQL-Job; 3 = SQL and REST-Job
/// </summary>
public byte? TypeId { get; set; }
/// <summary>
/// Filter by profile name (optional)
/// When specified, returns profiles with matching name (case-insensitive contains)
/// </summary>
public string? ProfileName { get; set; }
/// <summary>
/// Include associated SQL jobs in the result (optional)
/// Default: true
/// </summary>
public bool IncludeSqlJobs { get; set; } = true;
}
/// <summary>
/// Handler for GetProfileQuery
/// Retrieves profiles with optional filtering and uses AutoMapper for DTO mapping
/// </summary>
public class GetProfileQueryHandler : IRequestHandler<GetProfileQuery, List<CfgProfileDto>>
{
private readonly ICfgProfileRepository _profileRepository;
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)
{
_profileRepository = profileRepository;
_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)
{
IEnumerable<Domain.Entities.CfgProfile> profiles;
// If ID is specified, get single profile by ID
if (request.Id.HasValue)
{
var profile = request.IncludeSqlJobs
? await _profileRepository.GetByIdWithSqlJobsAsync(request.Id.Value, cancellationToken)
: await _profileRepository.GetByIdAsync(request.Id.Value, cancellationToken);
profiles = profile != null ? new[] { profile } : [];
}
// Otherwise, get profiles with filters
else
{
// Get all profiles with SQL jobs if requested
if (request.IncludeSqlJobs)
{
// If Active filter is specified and true, use optimized method
if (request.Active.HasValue && request.Active.Value)
{
profiles = await _profileRepository.GetAllActiveWithSqlJobsAsync(cancellationToken);
}
else
{
// Generic query with filters
profiles = await _profileRepository.FindAsync(p => true, cancellationToken);
}
}
else
{
profiles = await _profileRepository.GetAllAsync(cancellationToken);
}
// Apply filters
if (request.Active.HasValue)
{
profiles = profiles.Where(p => p.Active == request.Active.Value);
}
if (request.TypeId.HasValue)
{
profiles = profiles.Where(p => p.TypeId == request.TypeId.Value);
}
if (!string.IsNullOrWhiteSpace(request.ProfileName))
{
#if NET
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
}
}
// Use AutoMapper to map entities to DTOs
return _mapper.Map<List<CfgProfileDto>>(profiles.ToList());
}
}
}