Taking Control of Configuration in MVC6

Update

This article was updated to include changes from 6.0.0-beta8

If you’re coming to ASP.NET MVC6 from using prior versions, you’ll quickly begin to realize that things have changed. One of the major changes in this version of MVC is the lack of a Web.Config file. In this article, we’ll get an understanding of where configuration has moved, what are the new features and how to get values from configuration to the application.

Where’s my Web.Config?

XML configuration is a thing of the past. In ASP.NET MVC6 the Web.Config was completely removed and the configuration experience was overhauled. The new configuration consists of a variety of options including JSON-based files and environment variables.

configuration-mvc6

Let’s take a look at what is provided in a new MVC project.

New Configuration Options

The new configuration options are made available through the Startup routine in startup.cs. In the Startup method is a ConfigurationBuilder; it is new to MVC6 and provides a chain-able API where multiple configurations can be defined. Out-of-the-box, we are given two configuration options in Startup: appsettings.json and appsettings.{env.EnvironmentName}.json.

// Setup configuration sources.

    var builder = new ConfigurationBuilder()
            .SetBasePath(appEnv.ApplicationBasePath)
            .AddJsonFile("appsettings.json")
            .AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true);

The default configurations use AddJsonFile, which specifies that the configuration will be in JSON format. If JSON isn’t your preferred format, multiple configuration builders are available including: INI; Command Line; and Environment Variables. You can even create your own by implementing the IConfigurationBuilder interface.

In the standard MVC template we have appsettings.json and a second optional appsettings that is defined at runtime based on the EnvironmentName if available. This second appsettings comes in handy when an option needs to be overloaded.

Overloads

6605408121_5a25e49796_o

In previous versions of MVC, managing application settings for different environments such as development and production meant using transformations. In MVC6 this system has gone away in favor of configuration overloading.

When configurations are registered in the ConfigurationBuilder, the last value applied wins over any previous value of the same key.

//foo.json
{ "mykey" : "Foo" }

//bar.json 
{ "mykey" : "Bar" }

//startup.cs

var builder = new ConfigurationBuilder()
            .SetBasePath(appEnv.ApplicationBasePath)
                  .AddJsonFile("foo.json")
                  .AddJsonFile("bar.json");

//result
Configuration.Get("mykey"); // => Bar

Using configuration overloads we can have much greater control over how the application behaves in a given environment.

Getting values from configuration in MVC6

Getting values from configuration has also changed from previous version of MVC. Before values could be retrieved from the ConfigurationManager at the controller level, however in MVC6 the values are available to the application through dependency injection.

If we look at the end of the Startup method, we’ll see that ConfigurationBuilder is used to create a single instance of IConfiguration when the Build method is called.

Configuration = builder.Build();

6605404425_bf69be992a_o

Once the configuration is built, the values are injected into the project in the ConfigureServices method. Inside the ConfigureServices method, settings are added using services.Configure<T>. Fetching the value from configuration can be done by convention when the key matches the property name or by specifying the key explicitly.

In this example MyOptions, a POCO with the property ConfigMessage is fetched from configuration and added to the services collection.

//MyOptions.cs (POCO)
public class MyOptions
{
    public string ConfigMessage { get; set; }
}

//appsettings.json
{
    "MyOptions": {
        "ConfigMessage": "Hello from appsettings.json"
    }
}

//Startup.cs

//convention
services.Configure<MyOptions>(Configuration.GetConfigurationSection("MyOptions"));
    
//explicit
services.Configure<MyOptions>(Configuration("MyOptions:ConfigMessage"));

The values are made available to the application’s controller by adding the options to any controllers constructor. Since we’re using the dependency injection that has already been made available in MVC6, we’ll just need to reference the value. Using IOptions<T> as an argument in the constructor will resolve the dependency, this works because the service provider is resolved for us in the creation of a controller instance.

//HomeController.cs
private readonly string configMessage;
public HomeController(IOptions<MyOptions> myConfigOptions)
{
    configMessage = myConfigOptions.Value.ConfigMessage;
}

public IActionResult Index()
{
    ViewData["Message"] = configMessage; //=> Hello from appsettings.json
    return View(); 
}

Putting it together… and in the cloud

6605415049_93bbbb81a1_o

Having seen how appsettings.json works, what overloads do, and how values are retrieved we can put it to use. We’ll continue with the MyOptions example and see how different overrides take place in scenarios like development, staging and cloud deployment on Azure.

Toggling Config Files

p>First let’s revisit ConfigurationBuilder in Startup.cs. The configuration builder has two configs, appsettings.json and appsettings.{env.EnvironmentName}.json. We can use the second config to override settings when specific environment variables are available. We will start by adding the following configs to our project.

//appsettings.development.json
{
  "MyOptions": {
    "ConfigMessage": "Hello from appsettings.development.json"
  }
}

//appsettings.staging.json
{
  "MyOptions": {
    "ConfigMessage": "Hello from appsettings.staging.json"
  }
}

//appsettings.production.json
{
  "MyOptions": {
    "ConfigMessage": "Hello from appsettings.production.json"
  }
}

Running the application with the default environment variables will use the ConfigMessage “Hello from appsettings.development.json”, this is because the default EnvironmentName is “development”. Changing the ASPNET_ENV value to “staging” or “production” will get the configuration from the corresponding config file. To see this in action using Visual Studio, open the project’s properties and change ASPNET_ENV value under the debug menu.

aspnet-env

Overriding using Environment Variables

In addition to toggling config files, overriding set values can also be done through environment variables. Environment variables are ideal for Azure deployment because they separate the server configuration from the project. Using environment variables for values such as database connection strings and API keys will insure that the deployed application is always using production values.

Environment variables are added to ConfigurationBuilder near the end of the Startup method using the AddEnvironmentVariables(); method.

public Startup(IHostingEnvironment env, IApplicationEnvironment appEnv)
    {
        // Setup configuration sources.

        var builder = new ConfigurationBuilder(appEnv.ApplicationBasePath)
            .AddJsonFile("appsettings.json")
            .AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true);

        if (env.IsDevelopment())
        {
            // This reads the configuration keys from the secret store.
            // For more details on using the user secret store see http://go.microsoft.com/fwlink/?LinkID=532709
            builder.AddUserSecrets();
        }
        builder.AddEnvironmentVariables();
        Configuration = builder.Build();
    } 

Setting a value in Azure can be done through the management portal from the application’s Configure menu. Under App Settings enter a key/value pair that corresponds to the value needed by the application. If the setting has a hierarchy, be sure to use the full name space for the key using : as a separator.

Continuing with the example, we can set the ConfigMessage by adding an environment variable with the key MyOptions:ConfigMessage and the value Hello from Azure.

azure

The application will only receive the value Hello from Azure when it is deployed, otherwise it will get its value from the config file.

Conclusion

Configuration has completely changed in MVC6. The new implementation follows a more modular development approach common throughout MVC6. In addition, the JSON format feels ubiquitous with modern web practices. The new style of configuration may come with a learning curve, but it allows for great flexibility.

All of the examples shown in this article can be found in the HelloConfig project on GitHub.

All images for this article are courtesy of VGB.Studios

Comments

  • Alex

    I have read many articles on the new config system in ASP.NET 5 / MVC 6, but I have not found an answer to this question. How do we specify deeply nested system config settings? For example the max file post size setting:

    I’m not sure if I’m using the wrong JSON dot notation in my config.json, but nothing seems to work. I would appreciate a nudge in the right direction. Thanks!

  • David Carr

    Hi Ed, Excellent breakdown of the subject. Could you add the runtime version used just for clarity. Thank you.

    • Ed Charbeneau

      Updated to Beta-8

  • metz2000

    Isn’t it stupid that there is a built in method to read ini files what nobody used for ages but there isn’t for web.config to allow easy migration? 😐

  • Josh Mouch

    So how do you access the configuration if you can’t create a POCO and use IOption like you did? “Data:*:ConnectionString” is a good example.

  • Sergey Sagan

    Where is the explanation to do this:

    {
    “MyOptions”: {
    “ConfigMessage”: “Hello from appsettings.development.json”
    “MySubSection”: {
    “ThisThing”: “This Value”
    }
    }
    }

    How do I get MySubSection mapped?