Configuration in .NET Core part 3/3
September 26, 2021
So one last thing I would like to discuss regarding configurations is how to implement a custom configuration source. In my sample, I will implement a configuration source that parses command-line arguments using a parsing library I prefer. Note that there already exists a command-line arguments extension from Microsoft, but I find it rather limited in terms of features. Whether you agree with me or not, it will still make for a good example of how to implement a custom configuration source. So let’s add the CommandLineParser Nuget package to our project file. For reference, I will be using version 2.8 of the library. Next, we add a class that will contain command line settings and will be used to override configuration settings:
public class CommandlineOption
{
[Option('s', "setting")]
public string MySetting { get; set; }
[Option('i', "intValue")]
public string MyIntValue { get; set; }
[Option("intArray")]
public IEnumerable<int> MyIntArray
{ get; set; } = Array.Empty<int>();
}
Let’s implement configuration provider:
public class CommandLineProvider : ConfigurationProvider
{
private readonly CommandlineOption _option;
public CommandLineProvider(CommandlineOption option)
{
_option = option;
}
public override void Load()
{
var data = new Dictionary<string, string>
{
[nameof(MySettings.MySetting)] = _option.MySetting,
[$"{nameof(MySettings.Subsection)}" +
$":{nameof(MySubsection.MyIntValue)}"]
= _option.MyIntValue
};
var subsectionArray = _option.MyIntArray.ToArray();
for (var i = 0; i < subsectionArray.Length; i++)
{
data[$"{nameof(MySettings.Subsection)}" +
$":{nameof(MySubsection.IntArray)}:{i}"]
= subsectionArray[i].ToString();
}
Data = data.Where(kv => !string.IsNullOrEmpty(kv.Value))
.ToDictionary(kv => kv.Key, kv => kv.Value);
}
}
In this case, I opted to inherit from ConfigurationProvider
, which in turn
allows me to only implement the Load method. In a nutshell, you need something
that implements the IConfigurationProvider
interface. Other points to mention
are:
CommandlineOption
class is serialized into dictionary- Note the usage of
:
to separate sections. Also, array entries are separated with a:
followed by the index.
To configure our provider, we need to implement IConfigurationSource
:
public class CommandLineSource : IConfigurationSource
{
private readonly CommandlineOption _option;
public CommandLineSource(CommandlineOption option)
{
_option = option;
}
public IConfigurationProvider
Build(IConfigurationBuilder builder)
{
return new CommandLineProvider(_option);
}
}
In our case configuration source just passes the command-line options to the provider. Now, all we need to do is to add the configuration source to our configuration builder.
var configBuilder = new ConfigurationBuilder()
.Add(new CommandLineSource(option));
Here the option
is our parsed command-line parameter. In terms of
implementation, serializing the already parsed command line settings is a
drawback. The benefit on the other hand is, that those end up merged with other
settings and then also follow the same precedence of a settings source based on
the order of registration. Another point to mention is, that since the command
line arguments don’t change after the start, we could have created the same
dictionary and use it as input to the in-memory configuration source. But then
we would have to skip the fun part of implementing a custom provider! 😉 Another
good example of how to implement a custom configuration provider can be found in
Microsoft docs.
Sources:
- Code samples
- Documentation on creating custom configuration provider
- Source code for various base implementations of providers in .NET
- Building Cross-platform Applications with .NET Core on Pluralsight