IOptions Validation doesn't fire until I call validate method with property explicitly in Asp.Net Core 3

Viewed 887

Recently i was using IOptions interface to read configuration in Asp.net core project and i found that my code doesn't show exception page until i call "validate" method explicitly with required property as you can see in below code.

appsettings.json

"DashboardSettings": {
"Header": {
  "Title": "Seguro De Coche"//,
  //"SearchBoxEnabled": true
}

},

DashboardSetting.cs

public class DashboardSettings
{
    public HeaderSettings Header { get; set; }
}
public class HeaderSettings
{
    public string Title { get; set; }

    [Required]
    public bool SearchEnabled { get; set; }
}

Startup.cs

services.AddOptions<DashboardSettings>().
            Bind(configuration.GetSection("DashboardSettings")).
            ValidateDataAnnotations();

In above case, "SearchEnabled" property required validation doesn't fire. and when i call validate methods explicitly with property, it fires. (see code below with validation method)

services.AddOptions<DashboardSettings>().
         Bind(configuration.GetSection("DashboardSettings")).
            ValidateDataAnnotations().
            Validate(v =>
            {
                return v.Header.SearchEnabled;
            });

options-validation-in-aspnet-core

so my question is, if my strongly type would have multiple configuration properties, then would i use all properties of class for validating them? If it is, it doesn't seem a good idea to me. Any suggestion on this please?

2 Answers

I don't know if this was an option back in .net core 3 but in .net core 6 you have to call

.ValidateOnStart()

on the optionsbuilder returned from AddOptions<>()

Otherwise, validation is called when the object is retrieved for the first time.

ValidateDataAnnotations method are not called until the IOptions is actually resolved, meaning you request the Value property of it. It is not enough even for DI to resolve the IOption for you. This validation is simply 'very lazy'

If you want this validation to happen earlier and you use ASP.NET Core 6, you can now add ValidateOnStart method.

If you need to use one of the previous version or want more control over the error handling, you can simply ensure that your IOptions are resolved earlier in the process, e.g. during Service Configuration.

To do that create a class, that depends on those options, and make it request their values. Add its instance in Startup.cs:

    public class ConfigurationValidator ( IOptions<MyConfig> MyConfig )
    {
        //no validation trigger yet
        MyConfig = myConfig;
    }

    public IOptions<MyConfig> MyConfig { get; }

    internal void Validate()
    {
        //validation happens in this line:
        _ = MyConfig?.Value;
    }

And now in Startup.cs

public void Configure(IApplicationBuilder app, ConfigurationValidator configValidator)
{
      try
      {
            configValidator.Validate();
      }
      catch (Exception ex)
      { 
           //log your errors 
      }
      //process continues here, or not...
}

This approach leaves full control to you so you can decide how to handle your error. But in fact you don't need the Validate method, as the whole logic can happen already in constructor and then Configure method will immediately stop the process and rethrow exception

Ahh, and of course you need to register your ConfigurationValidator as a service. Singleton or whatever.

Related