ASP.NET Core Configuration

Introduction

ASP.NET Core configuration differs greatly from standard ASP.NET.

Instead of web.config or any other way to set up the configuration we use built-in Configuration framework that comes with ASP.NET Core.

It is still key-value pairs collection at the end, but we can obtain those values from various sources. One of default sources is appsettings.json file that comes with all templates. ASP.NET Core 2 templates have configuration already filled with values from appsettings.json file, along with few other sources.

However, we can use and combine various sources for configuration settings (values):

  • Files (JSON, XML, INI)
  • Command-line arguments
  • Environment variables
  • In-memory .NET objects
  • Azure Key Vault

We can also create custom providers and plug them into the ASP.NET Core system. These providers can use different outputs.

If you wanna dive right into the code sample you can find the code here.

Let’s move on and see what is new in ASP.NET Core 2 since in this post we will be focusing on the configuration in ASP.NET Core 2.

 

Configuration changes in ASP.NET Core 2

With the arrival of ASP.NET Core 2, the Configuration became the first-class citizen of DI. That means that we can use IConfiguration anywhere in our app to get an instance of Configuration. That is what ASP.NET Core dependency injection container does for us.

Example of getting Configuration via DI is Startup class that comes with ASP.NET Core 2 templates (Razor Pages, MVC). Since Configuration is now set up during host building process (Program.cs) default template gets Configuration instance inside of Startup class via DI.

This is the default code for Startup class that comes with default Razor Pages / MVC template:

public class Startup
    {
        public Startup(IConfiguration configuration)
        {
            Configuration = configuration;
        }

        public IConfiguration Configuration { get; }

        // This method gets called by the runtime. Use this method to add services to the container.
        public void ConfigureServices(IServiceCollection services)
        {
            services.AddMvc();
        }

        // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
        public void Configure(IApplicationBuilder app, IHostingEnvironment env)
        {
            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
                app.UseBrowserLink();
            }
            else
            {
                app.UseExceptionHandler("/Error");
            }

            app.UseStaticFiles();

            app.UseMvc(routes =>
            {
                routes.MapRoute(
                    name: "default",
                    template: "{controller}/{action=Index}/{id?}");
            });
        }
    }

Notice IConfiguration injected to Startup constructor, which means that Configuration was already configured and ready to use before Startup kicked off. That is because Configuration is now already pre-configured in Program.cs before using Startup class.

We can, of course, inject IConfiguration anywhere in our app, including controllers or pages:

public class ConfigInject : PageModel
{
    public ConfigInject(IConfiguration config)
    {

    }
}

 

Default app configuration

In ASP.NET Core 1 we had configuration construction placed inside of Startup constructor. As we said, with ASP.NET Core 2 configuration is baked in ASP.NET Core now and it is available to be used throughout our app via DI. Furthermore, the default application configuration construction also happens inside of ASP.NET Core.

To be precise, this happens during the process of building web host, inside of Program.cs.

Take look at default content of Program class that comes with ASP.NET Core 2:

public class Program
{
    public static void Main(string[] args)
    {
        BuildWebHost(args).Run();
    }

    public static IWebHost BuildWebHost(string[] args) =>
        WebHost.CreateDefaultBuilder(args)
            .UseStartup<Startup>()
            .Build();
}

Magic happens at line 9, this is where default builder is created. That means where default configuration is constructed. That includes app configuration, logging, default server and few other settings.

Let’s take a look at the actual content of CreateDefaultBuilder from the ASP.NET Core source (GitHub):

public static IWebHostBuilder CreateDefaultBuilder(string[] args)
{
    var builder = new WebHostBuilder()
        .UseKestrel()
        .UseContentRoot(Directory.GetCurrentDirectory())
        .ConfigureAppConfiguration((hostingContext, config) =>
        {
            var env = hostingContext.HostingEnvironment;

            config.AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)
                  .AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true, reloadOnChange: true);

            if (env.IsDevelopment())
            {
                var appAssembly = Assembly.Load(new AssemblyName(env.ApplicationName));
                if (appAssembly != null)
                {
                    config.AddUserSecrets(appAssembly, optional: true);
                }
            }

            config.AddEnvironmentVariables();

            if (args != null)
            {
                config.AddCommandLine(args);
            }
        })
        .ConfigureLogging((hostingContext, logging) =>
        {
            logging.AddConfiguration(hostingContext.Configuration.GetSection("Logging"));
            logging.AddConsole();
            logging.AddDebug();
        })
        .UseIISIntegration()
        .UseDefaultServiceProvider((context, options) =>
        {
            options.ValidateScopes = context.HostingEnvironment.IsDevelopment();
        });

    return builder;
}

You can see that both appsettings.json and appsettings that is environment specific are added to configuration. Afterwards, environment variables and command line arguments are also added to the configuration.

 

Remove default configuration options

We can clear out Configuration sources list and that will nullify all the default configuration that ASP.NET Core host does for us. That is the code that we saw in CreateDefaultBuilder method.

public static IWebHost BuildWebHost(string[] args)
{
    return WebHost
        .CreateDefaultBuilder(args)
        .ConfigureAppConfiguration(
            (WebHostBuilderContext context, IConfigurationBuilder builder) =>
            {
                builder.Sources.Clear();
            })
        .UseStartup<Startup>()
        .Build();
}

The line builder.Sources.Clear(); effectively removes everything that is currently set in Configuration (Env variables, appsettings.json, in memory configs etc). That means we can’t use settings from appsettings.json until we add them to the sources again.

Combining sources – order matters

I will add a new environment variable inside of project properties:

I could do this change directly via launchSettings.json file, by modifying profiles section. Yes, everything you change inside of Debug section withing Visual Studio will be saved to launchSettings.json file.

I will be starting the app via Visual Studio, which means I wanna modify my IIS Express profile:

We could of course set environment variables via command line interface. However, this is just for demonstration purposes and for debug/development session.

Now, let’s say I have following configuration in my appsettings.json file:

 

Do notice that now I have two same keys in my 2 separate configurations. CoolestFramework is set to “Sails.js” in appsettings.json source and it is set to “ASP.NET Core” via environment variables.

So, what happens now? Conflict? Exception?

No. The source that is added last wins.

Whatever comes last will override the previous configuration (key value).

Let’s see it in action:

public static IWebHost BuildWebHost(string[] args)
{
    return WebHost
        .CreateDefaultBuilder(args)
        .ConfigureAppConfiguration(
            (WebHostBuilderContext context, IConfigurationBuilder builder) =>
            {
                builder.Sources.Clear();

                builder
                    .AddEnvironmentVariables()
                    .AddJsonFile("appsettings.json", optional: false, reloadOnChange: true);
            })
        .UseStartup<Startup>()
        .Build();
}

First, we clear existing sources that are configured by the ASP.NET Core host, by clearing out the builder.Sources list.

After that, we build the configuration and we first add environment variables and after that, we add appsettings JSON file and afterwards we add optional appsettings environment specific file.

public ConfigInject(IConfiguration config)
{
    string coolestFramework = config["CoolestFramework"];
}

This will result in coolestFramework variable having value “Sails.js”.

If we want “ASP.NET Core” (and we want that, don’t we?) to be our preferred output we would add environment variables last.

 

Default app configuration

ConfigureAppConfiguration is the extension method on top of the IWebHostBuilder interface that let us extend or override default configuration.

With version 2.0 IList<Sources> property, that represents a list of configuration sources became public. That means we can directly change the order of sources, add new things to sources or even remove all configuration sources. We usually do this kind of configurations via extension methods, but it is good to know that we can manually interact with the property.

This is what is currently added to those sources:

We see that ordering is as follows:

  • Memory configuration source
  • JSON configuration source (appsettings.json)
  • JSON configuration source (appsettings.Development.json)
  • Environment variables configuration source
  • Command line arguments configuration source

 

That means that if use default app configuration anything in appsettings.{Environment}.json will override values from appsettings.json.

Environment variables will override memory and appsettings.json/appsettings.{Environment}.json configuration.

At the end, command line arguments will override any of these, since they come in last.

 

 

Summary

Definitely check out the follow-up post to read about injecting settings to your code or making use of settings that reload in run-time.

I know you can’t wait to try all this in your favourite editor.

You can find the code at GitHub: https://github.com/Ibro/AspNetCoreConfiguration

Ibrahim Šuta

Software Consultant interested and specialising in ASP.NET Core, C#, JavaScript, Angular, React.js.