Want to build great APIs? Or become even better at it? Check our Ultimate ASP.NET Core Web API program and learn how to create a full production-ready ASP.NET Core API using only the latest .NET technologies. Bonus materials (Security book, Docker book, and other bonus files) are included in the Premium package!
In this introductory article, we are going to learn how ASP.NET Core configuration works, the basic configuration concepts, and a few different ways we can use to configure our application. Even by default, the configuration mechanism in .NET is pretty powerful, but it has a lot of advanced mechanisms that make it even more so.
Once we go through the basic concepts, we’ll tackle some of the advanced ones, but for now, let’s just see what comes out of the box when we create an ASP.NET Core project.
The source code for this article can be found on the ASP.NET Core Configuration repo on GitHub. If you wish to follow along, use the start branch. To check out the finished source code, check out the basic concepts branch.
Support Code Maze on Patreon to get rid of ads and get the best discounts on our products!Application configuration is a way of providing the initial settings to our application on its startup. We use configuration to easily set up our application in different ways, usually depending on the environment that it’s deployed on.
Configuration may come from multiple different sources and in a wide variety of forms like files, environment variables, or some kind of store like Azure Key Vault, or even from in-memory storage or as command-line arguments.
Whichever way(s) we choose, the configuration mechanism exists to help us create flexible applications, without the need to recompile our source code. By using the configuration data we determine the behavior of our applications at runtime.
Configuration in .NET Core is pretty powerful and it can be easily installed and used. The NuGet package containing the configuration is Microsoft.Extensions.Configuration and you can easily add it to any .NET Core project by typing:
PM> Install-Package Microsoft.Extensions.Configuration in the Package Manager Console in Visual Studio or
dotnet add package Microsoft.Extensions.Configuration if you prefer the dotnet CLI.
If you create an ASP.NET Core application, you don’t have to worry about it since it’s referenced by default.
Configuration in .NET Core is even more powerful with the use of sections, configuration providers, and the Options pattern. We’ll talk about all of these concepts in this article, as well as later on.
Configuration data is defined as a set of key-value pairs.
These are just examples and besides a value, each key contains information about which level of the hierarchy it’s in.
Is this material useful to you? Consider subscribing and get ASP.NET Core Web API Best Practices eBook for FREE!
Let’s see what that means.
The Configuration API reads the hierarchical data by flattening the structure using delimiters.
This means we can write something like this in our configuration file:
< "Logging": < "LogLevel": < "Default": "Information", "Microsoft": "Warning", "Microsoft.Hosting.Lifetime": "Information" >>, "AllowedHosts": "*" >
And later on, access it in our code by using the semicolon delimiter “:”.
"Logging:LogLevel:Default" – We can get the Default logging level
"AllowedHosts" – We’ll get “*” (any host) in this case
This means we can have multiple keys named “Default” since the organization is hierarchical.
Let’s extend our example:
Is this material useful to you? Consider subscribing and get ASP.NET Core Web API Best Practices eBook for FREE!
< "Logging": < "LogLevel": < "Default": "Information", "Microsoft": "Warning", "Microsoft.Hosting.Lifetime": "Information" >>, "OtherLoggingProvider": < "LogLevel": < "Default": "Debug" >>, "AllowedHosts": "*" >
Now we can access the same key within the different hierarchy:
"Logging:LogLevel:Default" – returns “Information”
"OtherLoggingProvider:LogLevel:Default" – returns “Debug”
If the value doesn’t exist, we’ll get a null as a result.
Instead of accessing the values directly, using delimiters, we can use the options pattern, and GetSection() and GetChildren() methods to get sections and children of a section. These mechanisms make the configuration much easier to use. We’ll see how to use them later on.
Let’s see what we get out of the box when we create an ASP.NET Core application.
When we create a new ASP.NET Core application, our Program.cs file looks like this:
public class Program < public static void Main(string[] args) < CreateHostBuilder(args).Build().Run(); >public static IHostBuilder CreateHostBuilder(string[] args) => Host.CreateDefaultBuilder(args) .ConfigureWebHostDefaults(webBuilder => < webBuilder.UseStartup(); >); >
While it may not look that spectacular, this does a few things for us. The CreateDefaultBuilder() method is particularly interesting to us.
It creates an instance of a HostBuilder , which implements IHostBuilder and that’s our program initialization utility. Besides that, it populates it with some default values, some of which are the default values gathered from different sources.
Is this material useful to you? Consider subscribing and get ASP.NET Core Web API Best Practices eBook for FREE!
The CreateDefaultBuilder() method adds the default configuration values to the application in this particular order:
These key-value pairs are stored inside the IConfiguration , and it plays a big role in our application later on.
Now that we know how the default configuration is populated in our application, let’s see how we can read it at runtime.
After a bit of theory, let’s do some coding.
First of all, we need a way to access our values.
Since dependency injection is turned on by default in our application we can do it simply by injecting our IConfiguration interface in our controller (or anywhere else). So let’s do exactly that. Let’s inject IConfiguration into the HomeController class.
When we open the HomeController , we can immediately see that the ILogger is already injected into the constructor:
private readonly ILogger _logger; public HomeController(ILogger logger)We can inject IConfiguration in exactly the same manner:
private readonly ILogger _logger; private readonly IConfiguration _configuration; public HomeController(ILogger logger, IConfiguration configuration)Now we can use the _configuration variable to access our values in the entire controller. Let’s create a dummy model first to populate it with our data. Let’s navigate to the Model folder and create a HomeModel class. This will be a simple class with a single property DefaultLogLevel :
public class HomeModel < public string DefaultLogLevel < get; set; >>
Let’s return to our controller, read our configuration, and send the data to our Home Index view:
public IActionResult Index() < var homeModel = new HomeModel < DefaultLogLevel = _configuration.GetValue("Logging:LogLevel:Default") >; return View(homeModel); >
And change our Home Index.html a bit:
@ < ViewData["Title"] = "Home Page"; >Welcome
Our default logging level is @Model.DefaultLogLevelLearn about building Web apps with ASP.NET Core.
Sure enough, our application now displays our configuration value:
As you can see, we’ve used a strongly typed GetValue() method to read our default logging level, using the same principle as before to navigate the hierarchy.
It’s as easy as that.
The GetSection() method helps us further by isolating separate sections or subsections of our configuration. To put that into perspective let’s look at our example once again:
Is this material useful to you? Consider subscribing and get ASP.NET Core Web API Best Practices eBook for FREE!
< "Logging": < "LogLevel": < "Default": "Information", "Microsoft": "Warning", "Microsoft.Hosting.Lifetime": "Information" >>, "OtherLoggingProvider": < "LogLevel": < "Default": "Debug" >>, "AllowedHosts": "*" >
In this case, “Logging” would be a section, and “LogLevel” is a subsection. Since we need just the LogLevel data, we can isolate it by returning just the LogLevel subsection in our HomeController Index action:
public IActionResult Index() < var logLevelSection = _configuration.GetSection("Logging:LogLevel"); var homeModel = new HomeModel < DefaultLogLevel = logLevelSection.GetValue("Default") >; return View(homeModel); >
The result is the same as before.
Although this is a small example, imagine more complex settings file like we see every day in a real-world project. It contains a lot of different sections, for various parts of our application. Getting sections and organizing them logically would be a crucial task to make our application more readable, and less dependent on hard coded strings.
A common usage of the GetSection() method can be seen in the extension method GetConnectionString() .
Let’s load it from the assembly:
public static string GetConnectionString(this IConfiguration configuration, string name)We can see that it’s just the implementation of the GetSection() method. It takes a name of the connection string and then tries to find it within the “ConnectionStrings” section.
That’s why our connection strings should be located within that section inside our appsettings.json file:
< "Logging": < "LogLevel": < "Default": "Information", "Microsoft": "Warning", "Microsoft.Hosting.Lifetime": "Information" >>, "ConnectionStrings": < "sqlConnection": "server=.; database=CodeMazeCommerce; Integrated Security=true" >, "OtherLoggingProvider": < "LogLevel": < "Default": "Debug" >>, "AllowedHosts": "*" >
We can get the string as easy as this now:
Is this material useful to you? Consider subscribing and get ASP.NET Core Web API Best Practices eBook for FREE!
Configuration.GetConnectionString("sqlConnection");
We recommend that you use a similar pattern to get other sections of your configuration. If you are not familiar with extension methods check out our article on them to learn how they work.
We’ve seen how we can extract our configuration data by using IConfiguration . But it does have its flaws.
Having to type sections and keys to get the values can be really repetitive and error-prone. We risk introducing errors to our code, and these kinds of errors can cost us a lot of time until we discover them since someone else can introduce them, and we won’t notice them since a null result is returned when values are missing.
To overcome this problem, we can bind the configuration data to strongly typed objects. To do that, we can use the Bind() method.
We can quickly create a simple container class for the configuration called LoggingLevelConfiguration , inside our Model folder:
public class LoggingLevelConfiguration < public string Default < get; set; >>
And now, let’s make some changes to our HomeController Index() method:
public IActionResult Index() < var logLevelConfiguration = new LoggingLevelConfiguration(); _configuration.Bind("Logging:LogLevel", logLevelConfiguration); var homeModel = new HomeModel < DefaultLogLevel = logLevelConfiguration.Default >; return View(homeModel); >
And once again, we can run the project and make sure that result is the same.
Is this material useful to you? Consider subscribing and get ASP.NET Core Web API Best Practices eBook for FREE!
As you can see, instead of using GetValue() or GetSection() methods we’ve bound our configuration data to the LoggingLevelConfiguration section directly, and we’re accessing the configuration data by calling the Default property of that class.
Pretty neat, huh?
There are two things to note here though. First is that the names of the configuration data keys and class properties must match. The other is that if you extend the configuration, you need to extend the class as well, which can be a bit cumbersome, but it beats getting values by typing strings.
Any application that is meant to go to the production has at least two environments – development and production. Besides that, we can have other environments, like staging which is an environment where we can check if the application is working correctly before we deploy it to production.
Check out our article about using multiple environments in ASP.NET Core to learn about it in more detail.
As you might have noticed, our project template has two appsettings files. One is the default appsettings.json file, and another is appsettings.Development.json. In this file, we can override any value from the appsettings.json file and it will be used when we are working in the development environment.
The practical example of the usage would be having different configuration strings for production and development since we don’t want to mess up the production database during development.
For example, this would be a connection string for the development:
< "Logging": < "LogLevel": < "Default": "Information", "Microsoft": "Warning", "Microsoft.Hosting.Lifetime": "Information" >>, "ConnectionStrings": < "sqlConnection": "server=.; database=CodeMazeCommerce; Integrated Security=true" >, "OtherLoggingProvider": < "LogLevel": < "Default": "Debug" >>, "AllowedHosts": "*" >
And now we can create an appsettings.Production.json file in which we will change the connection string to something else:
That’s it. Once the application is running in the production environment (indicated by the ASPNETCORE_ENVIRONMENT environment variable), the production string from the apsettings.Production.json will be used instead of the default one.
In this article, we’ve gone through some basic concepts of ASP.NET Core application configuration. We’ve learned how to use change configuration settings for our application, use sections to logically group our configuration data, and read the hierarchical configuration structures.
We still haven’t touched upon the options pattern or configuration providers, which are our next topics of this series.
Want to build great APIs? Or become even better at it? Check our Ultimate ASP.NET Core Web API program and learn how to create a full production-ready ASP.NET Core API using only the latest .NET technologies. Bonus materials (Security book, Docker book, and other bonus files) are included in the Premium package!