Skip to content

tgharold/DotNetCore-ConfigurationOptionsValidationExamples

Repository files navigation

DotNetCore-ConfigurationOptionsValidationExamples

Examples of IOptions<T> validation in .NET Core

Table of Contents

General

The Options pattern in .NET Core has a few different ways of validating configuration/options. The solutions in this repository attempt to explore the different approaches and the good/bad of each.

In all of the demonstrated .NET Core approaches, validation of the options object instance does not happen until the first usage.

  • IOptions<T>: Validation only happens on the first access (even if the underlying configuration changes).
  • IOptionsSnapshot<T>: Validation happens on every new scope (usually every request).
  • IOptionsMonitor<T>: Runs validation on the next access after the underlying settings have changed.

For a discussion of which approach to use when injecting the options into a class, I recommend Andrew Lock's article. Keep in mind the object lifetimes for each approach and avoid captive dependencies (passing a scoped object like IOptionsSnapshot<T> into the constructor of a singleton / long-lived object).

One approach to dealing with the lazy-evaluation of validation rules would be to add those IOptions<T> as parameters to the Startup.Configure() and then instantiate a copy of every options object. This would give you a way to validate that anything in the appsettings*.json files (or environment variables) injected at startup are correct.

Examples

The main differences between the approaches to validation takes place in the AddValidatedSettings<T>() or AddSettings<T>() methods in the IServiceCollectionExtensions class.

Because the DatabaseOptions and other objects are passed into the WeatherForecastController constructor, simply running the project after editing appsettings.json file will let you experiment.

Example 1: ValidateDataAnnotations()

Uses the Microsoft Data Annotations approach of attribute-based validation on the C# model that represents the section in the configuration. This is the approach that I'd recommend for most use-cases as configuration validation is generally simple.

Note: Commit b1dac68 added support for recursively validating DataAnnotation attributes. The classes may have moved around in later commits, but the basic code remains the same.

Pros/Cons

  • Pro: The data annotation approach gives back a full list of all validation that failed.
  • Pro: Data annotation validation is simple to setup and works well for flat options.
  • Con: Data annotation via attributes quickly gets complicated when you have fields relying on each other.
  • Con: Data annotation validation does not validate sub-objects (without custom code).

Example 2: Validate()

Uses the .Validate() method and custom validation methods on the C# classes. Note that use of a marker/trait interface is not required, but it made it easier for me to call the validation method from within a generic method. The use of a generic method makes it difficult to construct a detailed error message.

If I wasn't using a generic method (AddValidatedSettings), it would be possible to separately validate each property of the Options object. This would allow per-property validation messages. An example of this can be seen in the old Aspnet/Options Github repository.

.Validate(o => o.Boolean)
.Validate(Options.DefaultName, o => o.Virtual == null, "Virtual")
.Validate(o => o.Integer > 12, "Integer");

I'm not a fan of this approach.

Pros/Cons

  • Con: It is harder to structure a useful message / object back to the caller.

Example 3: IValidateOptions

Uses the IValidateOptions interface on C# validation classes. See the DatabaseOptionsValidator class for an example validator. This was a really easy to implement approach.

What we found in practice is that doing validation like this is tedious and error-prone, but powerful. This approach could live along side the DataAnnotations appraoch in Example 1.

Pros/Cons

  • Pro: Allows you to send back multiple string messages.
  • Pro: Flexible / powerful.
  • Con: Have to wire up each pair of options class and the associated validator.

Reference Notes

About

Examples of IOptions<T> validation in .NET Core

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages