using FluentValidation; using FluentValidation.Results; using Microsoft.Extensions.Options; namespace Vegasco.WebApi.Common; public static class ValidatorExtensions { /// /// Asynchronously validates an instance of against all instances in . /// /// /// /// /// The failed validation results. public static async Task> ValidateAllAsync(this IEnumerable> validators, T instance) { var validationTasks = validators .Select(validator => validator.ValidateAsync(instance)) .ToList(); await Task.WhenAll(validationTasks); List failedValidations = validationTasks .Select(x => x.Result) .Where(x => !x.IsValid) .ToList(); return failedValidations; } public static Dictionary ToCombinedDictionary(this IEnumerable validationResults) { // Use a hash set to avoid duplicate error messages. Dictionary> combinedErrors = []; foreach (var error in validationResults.SelectMany(x => x.Errors)) { if (!combinedErrors.TryGetValue(error.PropertyName, out HashSet? value)) { value = ([error.ErrorMessage]); combinedErrors[error.PropertyName] = value; continue; } value.Add(error.ErrorMessage); } return combinedErrors.ToDictionary(x => x.Key, x => x.Value.ToArray()); } public static OptionsBuilder ValidateFluently(this OptionsBuilder builder) where T : class { builder.Services.AddTransient>(serviceProvider => { var validators = serviceProvider.GetServices>() ?? []; return new FluentValidationOptions(builder.Name, validators); }); return builder; } } internal class FluentValidationOptions : IValidateOptions where TOptions : class { private readonly IEnumerable> _validators; public string? Name { get; set; } public FluentValidationOptions(string? name, IEnumerable> validators) { Name = name; _validators = validators; } public ValidateOptionsResult Validate(string? name, TOptions options) { if (name is not null && name != Name) { return ValidateOptionsResult.Skip; } ArgumentNullException.ThrowIfNull(options); var failedValidations = _validators.ValidateAllAsync(options).Result; if (failedValidations.Count == 0) { return ValidateOptionsResult.Success; } return ValidateOptionsResult.Fail(failedValidations.SelectMany(x => x.Errors.Select(x => x.ErrorMessage))); } }