ReC/src/ReC.Application/DependencyInjection.cs
TekH cd53d7fbae Add validation pipeline with FluentValidation and MediatR
Introduced a validation pipeline using FluentValidation and
MediatR to enhance request validation. Added the following:

- Registered FluentValidation and MediatR dependencies in
  `ReC.Application.csproj`.
- Updated `DependencyInjection.cs` to register validators
  and MediatR behaviors, including `ValidationBehavior<,>`.
- Added `ValidationBehavior<TRequest, TResponse>` to handle
  request validation in the MediatR pipeline.
- Created `ReadOutResQueryValidator` to enforce validation
  rules for `ReadOutResQuery`.
- Refactored namespaces and imports for better organization.

These changes improve extensibility, maintainability, and
separation of concerns in the application.
2025-12-03 15:00:56 +01:00

116 lines
3.9 KiB
C#

using MediatR;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using ReC.Application.Common.Behaviors;
using ReC.Application.Common.Options;
using System.Reflection;
using FluentValidation;
namespace ReC.Application;
public static class DependencyInjection
{
public static IServiceCollection AddRecServices(this IServiceCollection services, Action<ConfigurationOptions> options)
{
var configOpt = new ConfigurationOptions();
options.Invoke(configOpt);
configOpt.EnsureRequiredServices();
configOpt.ApplyConfigurations(services);
services.AddValidatorsFromAssembly(Assembly.GetExecutingAssembly());
services.AddAutoMapper(cfg =>
{
cfg.AddMaps(Assembly.GetExecutingAssembly());
cfg.LicenseKey = configOpt.LuckyPennySoftwareLicenseKey;
});
services.AddMediatR(cfg =>
{
cfg.RegisterServicesFromAssembly(Assembly.GetExecutingAssembly());
cfg.AddOpenBehaviors([typeof(BodyQueryBehavior<,>), typeof(HeaderQueryBehavior<,>)]);
cfg.AddBehavior(typeof(IPipelineBehavior<,>), typeof(ValidationBehavior<,>));
cfg.LicenseKey = configOpt.LuckyPennySoftwareLicenseKey;
});
services.AddHttpClient();
return services;
}
public class ConfigurationOptions
{
#region Required Services
private readonly Dictionary<string, bool> _requiredServices = new()
{
{ nameof(ConfigureRecActions), false },
{ nameof(LuckyPennySoftwareLicenseKey), false }
};
internal void EnsureRequiredServices()
{
var missingServices = _requiredServices
.Where(kvp => !kvp.Value)
.Select(kvp => kvp.Key.Replace("Configure", string.Empty));
if (missingServices.Any())
throw new InvalidOperationException($"The following required services were not configured: {string.Join(", ", missingServices)}");
}
#endregion Required Services
#region Configuration
private readonly Queue<Action<IServiceCollection>> _configActions = new();
internal void ApplyConfigurations(IServiceCollection services)
{
while (_configActions.Count > 0)
{
var action = _configActions.Dequeue();
action(services);
}
}
#endregion Configuration
#region LuckyPennySoftwareLicenseKey
private string? _luckyPennySoftwareLicenseKey;
public string? LuckyPennySoftwareLicenseKey
{
get => _luckyPennySoftwareLicenseKey;
set
{
_luckyPennySoftwareLicenseKey = value;
if (value is not null)
_requiredServices[nameof(LuckyPennySoftwareLicenseKey)] = true;
}
}
#endregion LuckyPennySoftwareLicenseKey
#region ConfigureRecActions
public ConfigurationOptions ConfigureRecActions(Action<RecActionOptions> configure)
{
_configActions.Enqueue(services => services.Configure(configure));
if(_requiredServices[nameof(ConfigureRecActions)])
throw new InvalidOperationException("RecActionOptions have already been configured.");
_requiredServices[nameof(ConfigureRecActions)] = true;
return this;
}
public ConfigurationOptions ConfigureRecActions(IConfiguration configuration)
{
_configActions.Enqueue(services => services.Configure<RecActionOptions>(configuration));
if (_requiredServices[nameof(ConfigureRecActions)])
throw new InvalidOperationException("RecActionOptions have already been configured.");
_requiredServices[nameof(ConfigureRecActions)] = true;
return this;
}
#endregion ConfigureRecActions
}
}