In the previous article we had a look at Azure AD Authentication in ASP.NET Core 2.0.

This time, we will look at what has changed in configuration.

Configuration building moved to Program.cs

A major change is that instead of building the configuration in Startup, we do it in the Program class.

Here is a simple example:

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

    public static IWebHost BuildWebHost()
    {
        return new WebHostBuilder()
            .UseKestrel()
            .UseContentRoot(Directory.GetCurrentDirectory())
            .ConfigureAppConfiguration((builderContext, config) =>
            {
                IHostingEnvironment env = builderContext.HostingEnvironment;

                config.AddJsonFile("appsettings.json", optional: false, reloadOnChange: true)
                    .AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true, reloadOnChange: true);
            })
            .UseIISIntegration()
            .UseDefaultServiceProvider((context, options) =>
            {
                options.ValidateScopes = context.HostingEnvironment.IsDevelopment();
            })
            .UseStartup<Startup>()
            .Build();
    }
}

Take note of the ConfigureAppConfiguration function call:

.ConfigureAppConfiguration((builderContext, config) =>
{
    IHostingEnvironment env = builderContext.HostingEnvironment;

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

The function takes a single argument, which is a callback that takes a WebHostBuilderContext and an IConfigurationBuilder.

You can configure configuration as usual in there.

But wait! How can we access the built configuration in Startup?

The new Startup constructor looks like this:

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

public IConfiguration Configuration { get; }

Quite neat right? Now the Startup class is not defining its configuration, which means we can use alternative configurations very easily.

You don't need to inject the configuration to Startup if you don't use it there somehow. Before you might have added it to the service collection to inject it into other components. It is now done for you by WebHostBuilder, so that has become unnecessary.

WebHost.CreateDefaultBuilder

ASP.NET Core 2.0 offers a nice convenience method for creating a very typical configuration for an application.

Instead of writing this:

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

    public static IWebHost BuildWebHost(string[] args)
    {
        return 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();
            })
            .UseStartup<Startup>()
            .Build();
    }
}

You can just write:

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

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

You can check the source code for this convenience method here.

So what does it do for configuration?

It registers the following configuration sources:

  1. appsettings.json
  2. appsettings.environment.json (e.g. appsettings.Development.json)
  3. User secrets for the current assembly (if in Development environment)
  4. Environment variables
  5. Command-line arguments (if not null)

This makes it quite a lot easier to create a typical configuration for an ASP.NET Core app.

Do note you can call ConfigureAppConfiguration again after creating the default builder. Doing so will not replace the default configuration, but add on top of it.

E.g.:

public static IWebHost BuildWebHost(string[] args) =>
    WebHost.CreateDefaultBuilder(args)
        .ConfigureAppConfiguration((builderContext, config) =>
        {
            config.AddXmlFile("appsettings.xml", optional: true, reloadOnChange: true);
        })
        .UseStartup<Startup>()
        .Build();

Thanks for reading! Hopefully this article helps you, and don't hesitate to leave a comment if you have questions.

Related links