When we're new or have less contact with some project is hard to understand all configuration possibilities.
Option pattern is the most appropriate way to inject environment settings options into the application. isolating the options by scenario is adhering to important software engineering principles, such as Principle of Interface Segregation (ISP) and Separation of Concerns.
Validate the options is a good way to indicate what or how the definitions are out of the expectations, But until .NET 5 the interruption provided from validation just was thrown in run time.
From .NET 6 it is possible to enforce options validation check on start rather than in runtime, with the ValidatiOnStart()
extension method from OptionsBuilder<TOptions>
.
Below a simple example using ValidateDataAnnotations()
and ValidateOnStart()
:
public static class ServiceCollectionExtensions
{
public static OptionsBuilder<ApplicationOptions> ConfigureApplicationOptions(
this IServiceCollection services, IConfigurationSection section)
=> services
.AddOptions<ApplicationOptions>()
.Bind(section)
.ValidateDataAnnotations()
.ValidateOnStart();
}
And the usage in StartUp:
services.ConfigureApplicationOptions(
_configuration.GetSection(nameof(ApplicationOptions)));
ApplicationOptions
defining property as Required and URL:
public class ApplicationOptions
{
[Required, Url]
public string HttpClientAddress { get; init; }
}
AppSettings
setting the address wrongly on purpose:
{
"ApplicationOptions": {
"HttpClientAddress" : "localhost"
}
}
And then, when running the application, the interruption throws the error:
Microsoft.Extensions.Options.OptionsValidationException:
DataAnnotation validation failed for members: 'HttpClientAddress'
with the error: 'The HttpClientAddress field is not a valid
fully-qualified http, https, or ftp URL.'.
As we can see, the error is pretty clear and indicates what way we can take to solve the problem.
Another validation approache
Data Annotations is not the only way to validate the options. The Validate()
method provides a delegate Func<TOptions, bool>
that allows any kind of validation strategy.
This sample uses the library Fluent Validation with InlineValidator<T>
just to exemplify:
=> services
.AddOptions<ApplicationOptions>()
.Bind(section)
.Validate(options =>
{
var validator = new InlineValidator<ApplicationOptions>();
validator
.RuleFor(applicationOptions => applicationOptions.HttpClientAddress)
.NotNull()
.NotEmpty()
.WithMessage("HttpClientAddress must be informed");
return validator
.Validate(options, strategy => strategy.ThrowOnFailures())
.IsValid;
})
.ValidateOnStart();
Conclusion
This tool as simple as powerful to help to have a better development environment.
Top comments (1)
Parabéns pelo post, bem explicado e objetivo.