Exploring other configuration possibilities – Options, Settings, and Configuration

We can mix those configuration classes with extension methods. For example:

  • We can call the Configure and PostConfigure methods multiple times.
  • We can call the ConfigureAll and PostConfigureAll methods to configure all the options of a given TOptions.

Here, we use the PostConfigure method to demonstrate that. Let’s add the following two lines of code (highlighted):

const string NamedInstance = “MyNamedInstance”;
var builder = WebApplication.CreateBuilder(args);
builder.Services.PostConfigure<ConfigureMeOptions>(
    NamedInstance,
    x => x.Lines = x.Lines.Append(“Inline PostConfigure Before”)
);
builder.Services
    .AddSingleton<IPostConfigureOptions<ConfigureMeOptions>, ConfigureAllConfigureMeOptions>()
    .Configure<ConfigureMeOptions>(builder.Configuration
        .GetSection(“configureMe”))
    .Configure<ConfigureMeOptions>(NamedInstance, builder.Configuration
        .GetSection(“configureMe”))
    .AddSingleton<IConfigureOptions<ConfigureMeOptions>, ConfigureAllConfigureMeOptions>()
    //.AddSingleton<IConfigureNamedOptions<ConfigureMeOptions>, ConfigureAllConfigureMeOptions>()
    .AddSingleton<IConfigureOptions<ConfigureMeOptions>, ConfigureMoreConfigureMeOptions>()
;
builder.Services.PostConfigure<ConfigureMeOptions>(
    NamedInstance,
    x => x.Lines = x.Lines.Append(“Inline PostConfigure After”)
);
// …

The preceding code registers two configuration delegates that target our named instance. They both run in the post-configuration phase. So running the app and accessing the endpoint shows the order in which all lines are added:

{
  “defaultInstance”: {
    “title”: “Configure Me!”,
    “lines”: [
      “appsettings.json”,
      “ConfigureAll:Configure name: “,
      “ConfigureMore:Configure”,
      “ConfigureAll:PostConfigure name: “
    ]
  },
  “namedInstance”: {
    “title”: “Configure Me!”,
    “lines”: [
      “appsettings.json”,
      “ConfigureAll:Configure name: MyNamedInstance”,
      “ConfigureAll:Configure Not Default: MyNamedInstance”,
      “Inline PostConfigure Before”,
      “ConfigureAll:PostConfigure name: MyNamedInstance”,
      “Inline PostConfigure After”
    ]
  }
}

In the preceding JSON, we can see that the two highlighted lines are the ones we just added, loaded in order, and not applied to the default options.

There is one more possibility, which comes from the validation API. This is most likely an unintended side effect, but it works nonetheless.

The following code adds the “Inline Validate” line after the post-configuration phase:

builder.Services.AddOptions<ConfigureMeOptions>().Validate(options =>
{
    // Validate was not intended for this, but it works nonetheless…
   
options.Lines = options.Lines.Append(“Inline Validate”);
    return true;
});

On the separation of concerns aspect, we should stay away from this. However, knowing this may help you work around a post-configuration order issue one day.

Now that we know the options interface types, their lifetimes, and many ways to configure their values, it is time to validate them and enforce a certain level of integrity in our programs.

Project – OptionsValidation

Another feature that comes out of the box is options validation, which allows us to run validation code when a TOptions object is created. The validation code is guaranteed to run the first time an option is created and does not account for subsequent options modifications. Depending on the lifetime of your options object, the validation may or may not run. For example:

InterfaceLifetimeValidation
IOptionsMonitor<TOptions>SingletonValidate the options once.
IOptionsFactory<TOptions>TransientValidate the options every time the code calls the Create method.
IOptionsSnapshot<TOptions>ScopedValidate the options once per HTTP request (per scope).
IOptions<TOptions>SingletonValidate the options once.

 Table 9.4: the effect of validation on options lifetime.

I wrote three test cases in the ValidateLifetime.cs file if you are interested to see this in action.

We can create validation types to validate options classes. They must implement the IValidateOptions<TOptions> interface or use data annotations such as [Required]. Implementing the interface works very similarly to the options configuration.First, let’s see how to force the validation when the program starts.

Leave a Reply

Your email address will not be published. Required fields are marked *



         


          Terms of Use | Accessibility Privacy