In this episode we take a look at configuration in ASP.NET Core, the possible sources, how to read from them, the options pattern, wrapping up with development time secrets.
For the walk-through you can check the next video, but if you prefer a quick read, skip to the written synthesis.
The playlist for the whole series is here.
In ASP.NET Core the way configuration is managed really changed when comparing with ASP.NET, where we had the
web.config file (and others with the same format). In this post, we’ll take a look at this new configuration, and see how much more powerful it is.
For much more in depth information about this topic, be sure to check out the docs over here.
One of the great things introduced with ASP.NET Core configuration, is the ability to have multiple configuration providers, but being able to then use those configurations in the code in a consistent manner, regardless of their source.
So for instance, we can have configurations done through environment variables, command-line arguments and a JSON file, and then use them as if they all came from the same place.
(Copied from the docs) we currently have the following providers available:
|Provider||Provides configuration from|
|Azure Key Vault Configuration Provider||Azure Key Vault|
|Command-line Configuration Provider||Command-line parameters|
|Custom configuration provider||Custom source|
|Environment Variables Configuration Provider||Environment variables|
|File Configuration Provider||Files (INI, JSON, XML)|
|Key-per-file Configuration Provider||Directory files|
|Memory Configuration Provider||In-memory collections|
|User secrets (Secret Manager)||File in the user profile directory|
As you can see in the table above, we can also create our own provider, to access a different configuration format or location, but that’s out of scope for this post, we’ll just play a bit with some of the existing providers.
To provide a glimpse of what we can do with ASP.NET Core’s configuration infrastructure, in this post we’ll play a little bit with the command-line, file and user secrets providers.
The first thing we need to do is configure the configuration providers we want to use (or as we’ll see, not really 😛).
To configure the providers, we go into the
Program class and where we’re creating the
IWebHostBuilder, we can call the
ConfigureAppConfiguration method to setup the providers we want.
As we saw in previous episodes, the
CreateDefaultBuilder method already comes with a lot of things setup out of the box, and configuration providers are one of them. If we take a look at the method’s source, we can see what’s already there.
(complete source on GitHub)
By looking at the source, we can see that 5 providers are configured:
- 2 for JSON files, the first one named
appsettings.jsonand the other the same plus the name of the current environment
- a user secrets provider, only when in a development environment
- environment variables provider
- command line arguments provider
An important thing to point out is that the order in which the providers are configured is relevant. An added provider overrides any previous one’s configuration with the same key. For instance, if we set a value in
appsettings.json but then we want a different one in development mode, we simply put a configuration with the same key in
appsettings.development.json. Likewise for other providers.
Given these providers are already setup, for this post’s objective, no need to configure anything else.
Creating some configurations
Before we see how to use the configurations in the code, it’s probably a good idea to start by creating some 😀
Let’s start by creating a couple of JSON files in the root of the project -
appsettings.development.json - and add the following contents to them.
To pass in some configurations through the command line we can do
dotnet run -- --SomeRoot:SomeSubRoot:CmdLineKey 13579. The colon is used to define a hierarchy, so the equivalent in JSON would be:
If you’re using an IDE to run the application, there’s probably a menu somewhere that lets you pass in command line arguments.
- In Visual Studio 2017 you would -> right click project -> “Properties” -> “Debug” -> “Application Arguments” (note that this doesn’t work when using IIS Express)
- In JetBrains Rider, which is what I’m using, you go into the run configurations, and add the arguments into the “Program arguments” input
Now that we have some configurations to access, let’s get to it.
Let’s start with the simplest (but not great) way to access the configuration, using
Startup class, or even in our controllers or services, we can get an
IConfiguration injected, and use it to access the configurations. Then we can, for instance, do the following to get some values:
Even though this works, and for really simple stuff may be enough, it’s not great, and spreading strings like
"SomeRoot:SomeSubRoot:SomeKey" around the application isn’t very nice (even if we put it in constants).
What would be much nicer than this way of accessing our configurations, would be to have classes that represent them, providing a much cleaner and type safe way to access those values. To solve this problem, enter the options pattern.
The options pattern introduced in ASP.NET Core allows us to easily bind the configurations to POCOs. To map what we setup previously, we create a couple of classes:
Then to bind the configurations to these classes, we can add the following line to
Then to use it, we can go into the
GroupsController and add
IOptions<SomeRootConfiguration> to use our nicely structured configuration class. Alternatively, we can inject
IOptionsSnapshot<SomeRootConfiguration>, which allows the application to load configurations changed at runtime.
Avoiding IOptions injection
Even though the options pattern is pretty great improvement to what we saw before it, it can be further improved.
Unless for the reloading capabilities, injecting
IOptions isn’t really useful, and adds an extra dependency that’s really not required. When injecting into controllers, this extra
using isn’t a big deal, as we’re already in ASP.NET Core MVC land. But what about injecting it into other services/classes? Forcing a dependency just to inject configurations in other libraries, that may be used in a non ASP.NET Core application (e.g. a console application, a web application developed using a different web framework) is a harder pill to swallow.
Luckily, it’s an easy problem to solve. For a more detailed discussion on this you can check this excellent article, but I’ll quickly walk through a solution here.
Instead of using the
services.Configure method, we can bind the configuration directly to the class, and then inject it as a singleton. We can do that as follows:
This solves our problem and now, in the controller, we can simply inject a
To avoid repeating this code for all types of configurations we want to add, we can create an extension method to handle this logic.
And we can replace the previous configuration registration with:
Which now looks pretty much like the usual configuration registration, but without the
Another interesting configuration provider we should take a look at is the user secrets provider.
The user secrets provider is used during development to keep secrets from ending up in source control, like API keys, connection strings and things like that. In production you would probably use other means of providing those kinds of configurations, like environment variables or services to keep secrets (like Azure Key Vault).
User secrets are not encrypted, they’re simply stored in a different path to avoid adding them to source control by accident. For a lot more info on user secrets, head on to the docs.
Before adding secrets to the application, we need to add an identifier to it, so when starting the application can fetch from the secrets store the correct configurations. To do this, head into the application’s
csproj and add a GUID inside an
UserSecretsId element below the
In this case, we go into
CodingMilitia.PlayBall.GroupManagement.Web.csproj and do:
Now we can add some secrets by doing
dotnet user-secrets set "DemoSecrets:SomeKey" "02468".
To access the configured secrets, we do exactly the same as in the other types of providers, so I just created another POCO and configured it like we saw in the previous section.
Pretty neat I would say 😀
That’s a wrap for this intro to ASP.NET Core configuration. As usual, there’s a lot more to explore, but I think this covers the bases. Be sure to head on to the docs for a lot more information (the documentation for ASP.NET Core is pretty great in general).
The source code for this post is here.
Please send any feedback you have, so the next posts/videos can be better and even adjusted to more interesting topics.
Thanks for stopping by, cyaz!