Dictionary-style settings as IOptions

I love how we can use appsettings.json files to configure applications in the .NET Core platform. The JSON-format feels a lot less bloated than the old XML appSettings config I used to work with.

In this blog I'll explore how to load a dictionary-style settings class as an IOption. This can be very useful when working with dependency injection.

JSON dictionary

The other day I was working on an application that downloads podcasts based on a list of URLs. I specified the sources as config in the appsettings.json file like this:

{
    "Sources": {
        "echt-gebeurd": "https://rss.art19.com/echt-gebeurd",
        "radio-doc": "http://feeds.ntr.nl/radio_doc?format=xml",
        "radio-rep": "http://podcast.npo.nl/feed/reporter-radio.xml"
    }
}

Hook 'em up!

Now how would we load this into a settings object? .NET doesn't support this kind of class natively. Fortunately, it isn't hard to set this up with an extension method. Let's create the settings class:

public class SourceSettings: Dictionary<string, string>
{
}

Next, we'll add a static extension method that takes an IConfigurationSection and loads it into the IServiceCollection. This extension method will do the heavy lifting for us.

public static IServiceCollection ConfigureDictionary<TOptions>(
    this IServiceCollection services,
    IConfigurationSection section
) where
    TOptions : class, IDictionary<string, string>
{
    var values = section
        .GetChildren()
        .ToList();

    services.Configure<TOptions>(x =>
        values.ForEach(v => x.Add(v.Key, v.Value))
    );

    return services;
}

Now hook up the settings using the new extension method:

services.ConfigureDictionary<SourceSettings>(
  configuration.GetSection("Sources")
);

Now all you need to do is use it, by adding the settings as IOptions<SourceSettings> object to the constructor or

So... how about a list of values?

If we can do a Dictionary<string, string>, can we do a List<string>-type of settings as well? It turns out the answer is: yes! I don't have a good use-case for this, but it is nice to know that there are options!

Add the following extension method:

public static IServiceCollection ConfigureList<TOptions>(
    this IServiceCollection services,
    IConfigurationSection section
) where
    TOptions: class, IList<string>
{
    var values = section
        .GetChildren()
        .ToList();

    services.Configure<TOptions>(x =>
        values.ForEach(v => x.Add(v.Value))
    );

    return services;
}

We use the Value and ignore the Key. If you debug, you'll see that the keys are actually the indexes as strings (0, 1... n). To use this type of setting, create a section:

{
    "SupportedLanguages": [
        "NL",
        "EN",
        "ES"
    ]  
}

Create a class that will hold the values:

class SupportedLanguageSettings: List<string>
{
}

And hook it up:

services.ConfigureList<SupportedLanguageSettings>(
  configuration.GetSection("SupportedLanguages")
);

The last step is using the setting through dependency injection.

Wrap up

It turns out that it is easy to use a different setting type than a plain and boring object: we can use dictionaries and lists with some extension-method-magic.

I love the fact that we can leverage everything JSON has to offer use. I agree that some of these structures might not be very useful to most projects, but it is nice to know you have options!

Some settings-type might be too eccentric for your use-case.
  1. Gabriel Pająk says:

    Hi,
    Actualy there is much simpler way to achieve this, just use .Bind method, then code should look as follows:
    services.Configure(options => {
    Configuration.GetSection(“Sources”).Bind(options );
    });
    You can totaly get rid of extension method :)

expand_less