Using built-in methods to configure and validate option
An important part of any asp.net web application is how we handle our project settings, and from my point of view, a very strong emphasis should be set on how to validate our settings.
Think about what happens when you miss setting a proper int configuration value, would its default be acceptable?
You can find a link to the demo using the following link: https://github.com/ramihamati/webapidemos/tree/main/WebApi.DemoOptions
You can find this article also on my medium page: https://medium.com/@hamatirami/how-to-configure-options-in-asp-net-4869f2efd01f
Following article sections:
- Using
Configure
to register an option - What’s behind the
Configure
method - Using
IConfigureOptions
to configure an option - Using
IValidateOptions
to validate the option values - Validate with annotations
Using Configure to register an option
Adding a configuration model in Asp.NET can be achieved as simply as creating a configuration model and then using the Configure
method
That’s great, now we can simply access our options by demanding the IOptions<OptionMongoDb>
from our dependency mechanism:
What's behind the Configure method
In the previous section, we added an option using the Configure
method, which registers an option service for our configuration. But what’s behind that method?
Decompiling that code we can see that this method registers the options services using AddOptions
and then it adds a service for our option as the interfaceIConfigureOptions.
Now this tells me that I don’t have to call myself the method services.AddOptions()
because it’s being made automatically, and no need to worry about these services being added multiple times because internally they use TryAdd
extension methods.
Using IConfigureOptions to configure an option
In the previous section we saw that internally when configuring an option, the framework registers the serviceIConfigureOption<TOption>
which is used to configure the option model. The Asp.NET framework uses a service that binds the option model to the configuration section.
We can have our custom implementation of this configuration service.
Let’s define a new option model and implement the configuration class:
We should make sure that we are registering the options services by calling services.AddOptions()
then we can register our new service:
Using IValidateOptions to validate the option values
We have multiple methods to validate our configuration model properties. In the previous section, I added some basic property validations in the IConfigureOption<>
service, but we also have a handy service called IValidateOption<>
service which can also be used for the same purpose.
With this method the validation is performed when a property on that object is called (not when the object is configured)
Validate with annotations
A nice feature is to use annotations to validate our options model.
We have to use the AddOptions<>
method which uses an OptionBuilder
internally that has more neat functionalities, like validating data annotations or validating at startup. We can also register some predicate validations (not as cool as using IValidateOptions
)
Let’s register our new option model with the validations in place
Now we can run the validations at startup by registering the options like:
Annoyingly I can’t see a way to bind the
IValidateOptions to the
OptionBuilder or making the
IValidateOptions to run at starttime instead of runtime like the
OptionBuilder allows
Also annoying is the fact that we have too many ways of describing the same thing. Wouldn’t it be nice to just have one way of registering, configuring and validating? Instead of focusing on our business logic, we have to struggle understanding all these different ways and behaviours.