❔ FluentValidation on startup
What's not okay so that it fails with:
Unhandled exception. System.InvalidOperationException: No service for type 'FluentValidation.IValidator`1[Sts.MarketData.Api.Models.BinanceConfiguration]' has been registered.
public static class OptionsBuilderFluentValidationExtensions
{
public static OptionsBuilder<TOptions> ValidateFluentValidation<TOptions>(
this OptionsBuilder<TOptions> optionsBuilder) where TOptions : class
{
optionsBuilder.Services.AddSingleton<IValidateOptions<TOptions>>(
provider => new FluentValidationOptions<TOptions>(optionsBuilder.Name, provider));
return optionsBuilder;
}
}
public class FluentValidationOptions<TOptions> : IValidateOptions<TOptions> where TOptions : class
{
private readonly IServiceProvider _serviceProvider;
private readonly string? _name;
public FluentValidationOptions(string? name, IServiceProvider serviceProvider)
{
_serviceProvider = serviceProvider;
_name = name;
}
public ValidateOptionsResult Validate(string? name, TOptions options)
{
if (_name != null && _name != name)
{
return ValidateOptionsResult.Skip;
}
ArgumentNullException.ThrowIfNull(options);
using var scope = _serviceProvider.CreateScope();
var validator = scope.ServiceProvider.GetRequiredService<IValidator<TOptions>>();
var results = validator.Validate(options);
if (results.IsValid)
{
return ValidateOptionsResult.Success;
}
var typeName = options.GetType().Name;
var errors = results.Errors
.Select(result => $"Fluent validation failed for '{typeName}.{result.PropertyName}' with the error: '{result.ErrorMessage}'.")
.ToList();
return ValidateOptionsResult.Fail(errors);
}
}
public static class OptionsBuilderFluentValidationExtensions
{
public static OptionsBuilder<TOptions> ValidateFluentValidation<TOptions>(
this OptionsBuilder<TOptions> optionsBuilder) where TOptions : class
{
optionsBuilder.Services.AddSingleton<IValidateOptions<TOptions>>(
provider => new FluentValidationOptions<TOptions>(optionsBuilder.Name, provider));
return optionsBuilder;
}
}
public class FluentValidationOptions<TOptions> : IValidateOptions<TOptions> where TOptions : class
{
private readonly IServiceProvider _serviceProvider;
private readonly string? _name;
public FluentValidationOptions(string? name, IServiceProvider serviceProvider)
{
_serviceProvider = serviceProvider;
_name = name;
}
public ValidateOptionsResult Validate(string? name, TOptions options)
{
if (_name != null && _name != name)
{
return ValidateOptionsResult.Skip;
}
ArgumentNullException.ThrowIfNull(options);
using var scope = _serviceProvider.CreateScope();
var validator = scope.ServiceProvider.GetRequiredService<IValidator<TOptions>>();
var results = validator.Validate(options);
if (results.IsValid)
{
return ValidateOptionsResult.Success;
}
var typeName = options.GetType().Name;
var errors = results.Errors
.Select(result => $"Fluent validation failed for '{typeName}.{result.PropertyName}' with the error: '{result.ErrorMessage}'.")
.ToList();
return ValidateOptionsResult.Fail(errors);
}
}
2 Replies
public class BinanceConfigurationValidator : AbstractValidator<BinanceConfiguration>
{
public BinanceConfigurationValidator()
{
RuleFor(x => x.PollingInterval).GreaterThan(0);
RuleForEach(x => x.Accounts)
.ChildRules(account =>
{
account.RuleFor(c => c.Name).NotEmpty().NotNull();
});
}
}
public class BinanceConfigurationValidator : AbstractValidator<BinanceConfiguration>
{
public BinanceConfigurationValidator()
{
RuleFor(x => x.PollingInterval).GreaterThan(0);
RuleForEach(x => x.Accounts)
.ChildRules(account =>
{
account.RuleFor(c => c.Name).NotEmpty().NotNull();
});
}
}
builder.Services.AddOptions<BinanceConfiguration>()
.BindConfiguration("Exchanges:Binance")
.ValidateFluentValidation()
.ValidateOnStart();
builder.Services.AddOptions<BinanceConfiguration>()
.BindConfiguration("Exchanges:Binance")
.ValidateFluentValidation()
.ValidateOnStart();
builder.Services.AddValidatorsFromAssemblyContaining<Program>();
Looks like nothing has happened here. I will mark this as stale and this post will be archived until there is new activity.