Named options – Options, Settings, and Configuration

Now, let’s explore named options by configuring two more instances of the MyOptions class. The concept is to associate a configuration of the options with a name. Once that is done, we can request the configuration we need.

Unfortunately, the ways we explore named options and most online examples break the Inversion of Control principle.

Why? By injecting an interface that is directly tied to a lifetime, the consuming class controls that part of the dependency.

Rest assured, we are revisiting this at the end of the chapter.

First, in the appsettings.json file, let’s add the highlighted sections:

{
  “defaultOptions”: {
    “name”: “Default Options”
  },
  “options1”: {
    “name”: “Options 1”
  },
  “options2”: {
    “name”: “Options 2”
  }
}

Now that we have those configs, let’s configure them in the Program.cs file by adding the following lines:

builder.Services.Configure<MyOptions>(
    “Options1”,
    builder.Configuration.GetSection(“options1”)
);
builder.Services.Configure<MyOptions>(
    “Options2”,
    builder.Configuration.GetSection(“options2”)
);

In the preceding code, the highlighted strings represent the names of the options we are configuring. We associate each configuration section with a named instance.Now to consume those named options, we have multiple choices. We can inject an IOptionsFactory<MyOptions>, IOptionsMonitor<MyOptions>, or an IOptionsSnapshot<MyOptions> interface. The final choice depends on the lifetime the consumer of the options needs. However, in our case, we use all of them to ensure we explore them all.

IOptionsFactory<MyOptions>

Let’s start with creating an endpoint where we inject a factory:

app.MapGet(
    “/factory/{name}”,
    (string name, IOptionsFactory<MyOptions> factory)
        => factory.Create(name)
);

The factory interface forces us to pass in a name that is convenient for us. When we execute the program, the endpoint serves us the options based on the specified name. For example, when we send the following request:

GET https://localhost:8001/factory/Options1

The endpoint returns the following JSON:

{
  “name”: “Options 1”
}

If we pass Options2 instead, we get the following JSON:

{
  “name”: “Options 2”
}

As simple as that, we can now choose between three different options. Of course, once again, we can leverage any other technique we know, like constructor injections.Let’s explore the next interface.

IOptionsMonitor<MyOptions>

We use the IOptionsMonitor interface similarly to the IOptionsFactory interface when we need named options. So, let’s start by creating a similar endpoint:

app.MapGet(
    “/monitor/{name}”,
    (string name, IOptionsMonitor<MyOptions> monitor)
        => monitor.Get(name)
);

The preceding code is almost the same as the factory one, but the IOptionsMonitor interface exposes a Get method instead of a Create method. This semantically expresses that the code is getting an options instance (singleton) instead of creating a new one (transient).Again, similarly, if we send the following request:

GET https://localhost:8001/monitor/Options2

The server returns the following JSON:

{
  “name”: “Options 2”
}

One difference is that we can access the default options as well; here’s how:

app.MapGet(
    “/monitor”,
    (IOptionsMonitor<MyOptions> monitor)
        => monitor.CurrentValue
);

In the preceding code, the CurrentValue property returns the default options. So, when calling this endpoint, we should receive the following JSON:

{
  “name”: “Default Options”
}

As simple as that, we can either access the default value or a named value. We explore one other scenario that the IOptionsMonitor interface supports after we cover the IOptionsSnapshot interface next.

IOptionsSnapshot<MyOptions>

The IOptionsSnapshot interface inherits the IOptions interface, contributing its Value property, and also offers a Get method (scoped lifetime) that works like the IOptionsMonitor interface.Let’s start with the first endpoint:

app.MapGet(
    “/snapshot”,
    (IOptionsSnapshot<MyOptions> snapshot)
        => snapshot.Value
);

It should be no surprise that the preceding endpoint returns the following default options:

{
  “name”: “Default Options”
}

Then the following parametrized endpoint returns the specified named options:

app.MapGet(
    “/snapshot/{name}”,
    (string name, IOptionsSnapshot<MyOptions> snapshot)
        => snapshot.Get(name)
);

Say we are passing the name Options1, then the endpoint will return the following options:

{
  “name”: “Options 1”
}

And we are done. It is quite simple to use the options as .NET does most of the work for us. The same goes for configuring options classes.But wait, our exploration isn’t over yet! Up next, we delve into the process of reloading options at runtime.

Leave a Reply

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



         


          Terms of Use | Accessibility Privacy