Skip to content

Instantly share code, notes, and snippets.

@marcin-burak
Created February 17, 2024 09:41
Show Gist options
  • Save marcin-burak/28d2a46b9bb2ed2ad7da50f552e77844 to your computer and use it in GitHub Desktop.
Save marcin-burak/28d2a46b9bb2ed2ad7da50f552e77844 to your computer and use it in GitHub Desktop.
ASP.NET options startup validation with FluentValidation
public static class OptionsBuilderFluentValidationExtensions
{
public static OptionsBuilder<TOptions> ValidateWithFluentValidation<TOptions>(this OptionsBuilder<TOptions> optionsBuilder) where TOptions : class
{
optionsBuilder.Services.AddSingleton<IValidateOptions<TOptions>>(serviceProvider =>
new FluentValidationOptions<TOptions>(optionsBuilder.Name, serviceProvider.GetRequiredService<IValidator<TOptions>>())
);
return optionsBuilder;
}
}
public sealed class FluentValidationOptions<TOptions>(string? name, IValidator<TOptions> validator) : IValidateOptions<TOptions> where TOptions : class
{
private readonly IValidator<TOptions> _validator = validator;
public string? Name { get; } = name;
public ValidateOptionsResult Validate(string? name, TOptions options)
{
if (Name is not null && Name != name)
{
return ValidateOptionsResult.Skip;
}
ArgumentNullException.ThrowIfNull(options);
var validationResult = _validator.Validate(options);
if (validationResult.IsValid)
{
return ValidateOptionsResult.Success;
}
var optionsTypeName = typeof(TOptions).Name;
var errors = validationResult.Errors.Select(error => error.ErrorMessage).ToArray();
return ValidateOptionsResult.Fail(errors);
}
}
internal static class Extensions
{
public static IServiceCollection AddOptionsByConvention<TOptions>(this IServiceCollection services, bool skipStartupValidation = false) where TOptions : class, new()
{
var options = services.AddOptions<TOptions>().BindConfiguration(GetConfigurationSectionName<TOptions>());
if (skipStartupValidation)
{
return services;
}
options.ValidateWithFluentValidation().ValidateOnStart();
return services;
}
public static TOptions GetOptionsByConvention<TOptions>(this IConfiguration configuration) where TOptions : class, new()
{
var options = Activator.CreateInstance<TOptions>();
configuration.Bind(GetConfigurationSectionName<TOptions>(), options);
return options;
}
private static string GetConfigurationSectionName<TOptions>() where TOptions : class, new()
{
var optionsTypeNameWithoutOptionsSuffix = typeof(TOptions).Name[..^7];
return optionsTypeNameWithoutOptionsSuffix;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment