refactor(application): consolidate query architecture with AutoMapper integration

- Add AutoMapper profile for CfgProfile and ProfileSqlJob entity-to-DTO mappings
- Consolidate GetProfileByIdQuery and GetAllActiveProfilesQuery into unified GetProfileQuery
- Implement flexible filtering with nullable query options (Id, Active, TypeId, ProfileName)
- Add IncludeSqlJobs option for optimized SQL job loading
- Move CfgProfileDto to Common/Dtos for better architecture alignment
- Add comprehensive unit tests for GetProfileQuery with multiple filter scenarios
- Move ISQLExecutor interface from Domain to Application layer
- Add GetByIdWithSqlJobsAsync and GetAllActiveWithSqlJobsAsync to repository
This commit is contained in:
2026-07-11 19:14:51 +02:00
parent 3f924b75b0
commit eaf24e05ee
15 changed files with 629 additions and 16 deletions

View File

@@ -0,0 +1,118 @@
using System;
using System.Collections.Generic;
namespace ECMJobRunner.Application.Common.Dtos
{
/// <summary>
/// Data Transfer Object for CfgProfile entity
/// Used for querying and returning profile data
/// </summary>
public class CfgProfileDto
{
/// <summary>
/// Profile ID
/// </summary>
public long Id { get; set; }
/// <summary>
/// Active / Inactive switch
/// </summary>
public bool Active { get; set; }
/// <summary>
/// Profile name
/// </summary>
public string ProfileName { get; set; } = null!;
/// <summary>
/// Profile type: 0 = ADSync; 1 = GraphQL; 2 = SQL-Job; 3 = SQL and REST-Job
/// </summary>
public byte TypeId { get; set; }
/// <summary>
/// Schedule in Cron format
/// </summary>
public string Schedule { get; set; } = null!;
/// <summary>
/// Optional description
/// </summary>
public string? Comment { get; set; }
/// <summary>
/// Created by
/// </summary>
public string AddedWho { get; set; } = null!;
/// <summary>
/// Created at
/// </summary>
public DateTime AddedWhen { get; set; }
/// <summary>
/// Modified by
/// </summary>
public string? ChangedWho { get; set; }
/// <summary>
/// Modified at
/// </summary>
public DateTime? ChangedWhen { get; set; }
/// <summary>
/// SQL Jobs associated with this profile
/// </summary>
public List<ProfileSqlJobDto>? SqlJobs { get; set; }
}
/// <summary>
/// Data Transfer Object for ProfileSqlJob entity
/// </summary>
public class ProfileSqlJobDto
{
/// <summary>
/// SQL Job ID
/// </summary>
public long Id { get; set; }
/// <summary>
/// Profile ID (foreign key)
/// </summary>
public long ProfileId { get; set; }
/// <summary>
/// Active / Inactive switch
/// </summary>
public bool Active { get; set; }
/// <summary>
/// Execution sequence order
/// </summary>
public short Sequence { get; set; }
/// <summary>
/// Job name
/// </summary>
public string? Name { get; set; }
/// <summary>
/// SQL query for pre-check validation
/// </summary>
public string? SqlCheckQuery { get; set; }
/// <summary>
/// Main SQL query to execute
/// </summary>
public string? SqlMainQuery { get; set; }
/// <summary>
/// API command to execute
/// </summary>
public string? ApiCommand { get; set; }
/// <summary>
/// Optional description
/// </summary>
public string? Comment { get; set; }
}
}

View File

@@ -0,0 +1,20 @@
using System.Threading;
using System.Threading.Tasks;
namespace ECMJobRunner.Application.Common.Interfaces
{
/// <summary>
/// Interface for executing SQL queries and mapping results to DTOs
/// </summary>
public interface ISQLExecutor
{
/// <summary>
/// Executes a SQL query and maps the result to the specified type
/// </summary>
/// <typeparam name="TResult">The type to map the query result to</typeparam>
/// <param name="sql">The SQL query to execute</param>
/// <param name="cancellationToken">Cancellation token</param>
/// <returns>The mapped result or null if no result</returns>
Task<TResult?> ExecuteQueryAsync<TResult>(string sql, CancellationToken cancellationToken = default);
}
}

View File

@@ -0,0 +1,23 @@
using AutoMapper;
using ECMJobRunner.Application.Common.Dtos;
using ECMJobRunner.Domain.Entities;
namespace ECMJobRunner.Application.Common.Mapping
{
/// <summary>
/// AutoMapper profile for CfgProfile and ProfileSqlJob mappings
/// Maps domain entities to DTOs
/// </summary>
public class ProfileMappingProfile : Profile
{
public ProfileMappingProfile()
{
// CfgProfile -> CfgProfileDto
CreateMap<CfgProfile, CfgProfileDto>()
.ForMember(dest => dest.SqlJobs, opt => opt.MapFrom(src => src.SqlJobs));
// ProfileSqlJob -> ProfileSqlJobDto
CreateMap<ProfileSqlJob, ProfileSqlJobDto>();
}
}
}