ASP.NET 5 Configuration - Microsoft Framework ConfigurationModel

Feb 27, 2015

Note: [Deprecated Content] This was written while ASP.NET and .NET Core was in beta (around beta5 or beta6). Since then a lot of stuffs have changed and moved around. Stuffs described below may or may not be true for current stable releases of ASP.NET Core - Configuration feature. I recommend not to follow along and get confused. Use official configuration documentation that explains it correctly - docs.asp.net

With the upcoming release of .NET 4.6 and ASP.NET 5 (currently in BETA phase) one namespace that is getting new look (re-written) is System.Configuration.

If you have not already read then I recommend you to take a look at getting started post by Louis DeJardin - ASP.NET vNext Moving Parts: IConfiguration

He has also explained other moving parts which are some interesting reads.

Why you ask?

Louis explains few reason why System.Configuration needs to change from the way it is, following summary is based on what he narrates (and what I understand):

New configuration subsystem...

  • Should be light-weight.
  • Should run everywhere, Cloud Optimized / Enabled.
  • Can work without using lots of #ifdef (common base for platform profile(s) it can support).
  • Application's responsibility to instantiate Configuration sub-system as they do for other subsystem like MVC, Identity or Razor etc. This also means you can have more than one instance of Configuration subsystem.

Apart from the above I see some more advantages to the new configuration model:

  • Ability to load configuration from multiple source type (I love this).
  • Extensibility - Load custom configuration sources, you do not need to stick to web.config ONLY anymore.
  • Option to precisely define configuration(s) for Development / QA / Validation / Staging / Production environment without introducing much complexity and in control of the application.

Comments open for more advantages (or disadvantages) I may have not seen.

What is it?

The new configuration resides in namespace Microsoft.Framework.ConfigurationModel and here are some of the important entities residing in this namespace that runs the show:

Important entities from Microsoft.Framework.ConfigurationModel namespace

Let's devote some lines to what these entities are responsible for

  • IConfiguration - Pretty obvious, this is the interface you will instantiate and will serve the configuration for you. This consist of indexer, Get, TryGet, Set and other methods to GET / SET / LOAD / COMMIT / RELOAD - key / value based configuration of your choice.

  • IConfigurationSource - The interface that defines contracts for a particular configuration source and specify calls for TryGet, Set and most important Load for any configuration source to be loaded into the configuration sub-system.

  • IConfigurationSourceContainer - Serves as a container for all configuration sources to reside. This is part that enables loading configuration from multiple sources in a single Configuration instance. It defines only one contract Add through which you load any configuration source that adheres IConfigurationSource into the collection of configuration sources.

  • Configuration - It implements the IConfiguration and IConfigurationSourceContainer container and holds to the configuration key-value data loaded by an application.

  • ConfigurationExtensions - This provides quick usable extensions to load specific configuration source over instance of IConfigurationSourceContainer (Configuration).

Extension methods follows AddXxx pattern where Xxx can be name of a configuration source type.

Currently Microsoft.Framework.ConfigurationModel contains 6 configuration source types implemented for you to load and use:

  • MemoryConfigurationSource - This one does not have a built-in add / load extension (AddMemoryConfiguration or something) yet (as of BETA-3), but you can use this to load key / value based configuration from IEnumerable<KeyValuePair<string, string>> type instance.

  • IniFileConfigurationSource - This reads INI file for key / value based configurations into your configuration sub-system. Support for INI file, pretty cool.

  • CommandLineConfigurationSource - Reads command line configuration / switch passed when application starts.

  • EnvironmentVariablesConfigurationSource - This source will read configuration values from EnvironmentVariable set in the system. This can be handy as Azure Websites provide way to set environment variables for your website instance.

  • JsonConfigurationSource - Loads and adds configuration from JSON based files.

  • XmlconfigurationSource - Loads and adds configuration from XML based files.

How to do it?

Let us do some configuration dance. Creating instance of configuration class would be pretty simple, duh:

IConfiguration configuration = new Configuration();
Adding MemoryConfigurationSource

At the time of writing this post there is no built-in extension to IConfigurationSourceContainer that would load configuration from MemoryConfigurationSource.

Still if you wish you can use MemoryConfigurationSource using following code and load your in-memory configuration:

((IConfigurationSourceContainer)Configuration)
    .Add(new MemoryConfigurationSource(
    new List<KeyValuePair<string, string>> {
		new KeyValuePair<string, string>("mem-key1", "mem-value1"),
		new KeyValuePair<string, string>("mem-key2", "mem-value2")
    }));

Get those configuration values using:

var someConfiguration1 = Configuration.["mem-key1"];
var someConfiguration2 = Configuration.Get("mem-key2");
Adding IniFileConfigurationSource

INI based configuration files can be loaded using shipped extension as follows:

var configuration = new Configuration().AddIniFile("path\\to\\your\\configuration-ini-file.ini");

Sample INI file for reference:

[ini-sec]
ini-key1=ini-value1
ini-key2=ini-value2

Here [ini-sec] is name of a INI section and under that are some key / value configuration.

These loaded key - value can be reterived as:

var someConfiguration1 = Configuration["ini-sec:ini-key1"];
var someConfiguration2 = Configuration.Get("ini-sec:ini-key2");

Note the : notation to specify parent / sub key in this case.

Adding CommandLineConfigurationSource

Command line configuration can be passed when you k run your application. This can be done for ASP.NET 5 Console Application and I do not see or know how can we pass command line arguments to Startup.cs of ASP.NET 5 Web Application. May be someone from the ASP.NET team might come-up with an answer to that.

var configuration = new Configuration().AddCommandLine(commandLineArgumentArray);

Some of the valid command line key / value examples:

CmdArgKey1=Value1
--CmdArgKey2=Value2
/CmdArgKey3=Value3
-CmdArgKey4=Value4
--CmdArgKey5
Value5

You can also load CommandLineConfigurationSource with switchMappings (aliases for your command line keys) as IDictionary<string, string>.

Following switchMapping defines mapping for above mentioned example command line key - --CmdArgKey2 and -CmdArgKey4 for your reference.

var switchMappings = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase)
    {
        { "--CmdArgKey2", "cak2" },
        { "-CmdArgKey4", "cak4" },
    };

Currently there is no built-in extension for passing switchMapping (optional constructor parameter) so you can load CommnandLineConfigurationSource with switchMapping as follows:

((IConfigurationSourceContainer)Configuration).Add(new CommandLineConfigurationSource(commandLineArguments, switchMappings: switchMappingsInstance));

Values can be retrieved as following:

var someConfiguration1 = Configuration["CmdArgKey1"];
var someConfiguration2 = Configuration.Get("CmdArgKey2");
var someConfiguration2 = Configuration["cak2"];
var someConfiguration1 = Configuration.Get("CmdArgKey3");
var someConfiguration2 = Configuration["CmdArgKey4"];
var someConfiguration1 = Configuration.Get("cak4");
var someConfiguration2 = Configuration["CmdArgKey5"];

Note:

  • You can use the switch mapping key instead of original key when the same is define and passed via switchMappings.
  • You cannot pass / key type switch mappings, it will throw ArgumentException
  • You cannot pass same key more than once in switch mappings, it will throw ArgumentException
  • If you pass two or more values for same key, configuration will overwrite all previous value(s) with the one passed last for that key (i.e. if you pass key1 with value1 and then key1 with value2 then value1 will be overwritten with value2).
  • Any key / value passed in command line with - as prefix are ShortSwitch and requires switchMapping entry for the same to be passed for that key when source is loaded, else FormatException will be thrown.
Adding EnvironmentVariablesConfigurationSource

This can be used to load or read configuration key / value you have set as your Environment Variables, in your system globally or in your application from project's property page.

Snapshot on how to set environment variables from your project > Properties > Debug > Environment Variables:

Set environment variable from VS 2015 - Project property page

Two env. variable configuration settings can be fetched as:

var someConfiguration1 = Configuration["env_var_key1"];
var someConfiguration2 = Configuration["env_var_key2"];

EnvironmentVariablesConfigurationSource is Azure environment / connection string aware. You can set your MS SQL, MySQL, SQL Azure DB or custom connection string with provider.

Connection string key values should start with following:

  • MySQL => MYSQLCONNSTR_
  • MS SQL => SQLCONNSTR_
  • SQL Azure DB => SQLAZURECONNSTR_
  • Custom DB => CUSTOMCONNSTR_

Example key / value environment variable configuration would be:

Key => SQLCONNSTR_devlocal
Value => Server=localhost;Database=test_db;Trusted_Connection=True;

After loading (without prefix) EnvironmentVariablesConfigurationSource using AddEnvironmentVariables() above sample can be retrieved as follows:

var someConfiguration1 = Configuration["Data:devlocal:ConnectionString"];
/// someConfiguration1 - value will be - Server=localhost;Database=test_db;Trusted_Connection=True;

Azure aware env. variables KEY will be converted to Data:YOUR_KEY_IDENTIFIER:ConnectionString format. If your Azure aware key is not of type CUSTOMCONNSTR_ then you can also get the connection string provider value:

var someConfiguration2 = Configuration["Data:devlocal:ProviderName"];
/// someConfiguration2 - value will be - System.Data.SqlClient

If you load your EnvironmentVariablesConfigurationSource with _prefix extension then it will load only configuration with provided prefix.

You can also use Data: prefix to load Azure aware connection strings and provider values.
Thus above configuration fetch will change to:

var someConfiguration1 = Configuration["devlocal:ConnectionString"];
var someConfiguration2 = Configuration["devlocal:ProviderName"];
Adding JsonConfigurationSource

JSON based configuration can be loaded using .AddJsonFile("test.json"). This assumes that test.json file is in the root directory of the application (or can also provide absolute path).

Do not forget to import / add Microsoft.Framework.ConfigurationModel.Json namespace / package (in your project.json file > dependencies).

Sample JSON (test.json) file:

{
    "Application":{
		"Mode": "SampleJsonValue"
    }
}

You can Get this configuration as:

var someConfiguration1 = Configuration["Application:Mode"];
Adding XmlconfigurationSource

The old and familiar XML based configuration can be loaded using .AddXmlFile("test.xml"). This assumes that test.xml file is in the root directory of the application (or can also provide absolute path).

Sample XML (test.xml) file:

<note>
    <from>Jsinh</from>
	<body subject="Hello world" />
</note>

Following XML configuration values can be read as:

var someConfiguration1 = Configuration["from"];
/// value will be - Jsinh
var someConfiguration2 = Configuration["body:subject"];
/// value will be - Hello world

This sample XML will throw FormatException (duplicate body element - key):

<note>
    <from>Jsinh</from>
	<body subject="Hello world" />
	<body subject="Hello world again" />
</note>

Are we done now?

Not quite yet, you can roll out your own configuration source and load / add them to your configuration sub-system instance very easily (this is the extensibility aspect of new configuration sub-system)

I always wanted to add / load cats source to my configuration (what else do you expect from one who write such a long post):

Custom configuration source load / read / get / set implementation (mine is called - CatsConfigurationSource):

namespace Microsoft.Framework.ConfigurationModel
{
    public class CatsConfigurationSource : BaseConfigurationSource
    {
        public WebConfigConfigurationSource(string[] initialPackOfCats)
        {
            //// Implementation omitted cause cats loading is silly actually.
        }

        public override void Load()
        {
            //// Implementation omitted cause cats loading is silly actually.
            ReplaceData(yourLoadedCatsConfigurationData); // Input type Dictionary<string, string>
        }
    }
}

Second class to create is (not mandatory, but if you are fan of extensions and clean loading / adding of configuration source) - CatsConfigurationExtensions:

namespace Microsoft.Framework.ConfigurationModel
{
    public static class CatsConfigurationExtensions
    {
        public static IConfigurationSourceContainer AddFancyCats(
        	this IConfigurationSourceContainer configuration,
            string[] initialPackOfCats)
        {
            configuration.Add(new CatsConfigurationSource(initialPackOfCats));
            return configuration;
        }
    }
}

Phew, now you can add fancy cats to your configuration as : .AddFancyCats(someKickStartCats);

Finally it's over!!

Nope, not yet. Remember:

  • All configuration keys are CASE-INSENSITIVE. This means key string Data:devlocal and data:DEVlocal are same thing.
  • If you load / add same key from different source, you will always receive the key - value from the source that was loaded / added last.

Comments open if you find other final remember to add.

I am not listening, la la la...

Moving parts - Configuration sub-system - .NET - ASP.NET 5 are all in beta phase (currently in beta 3) and above information (some part or entirely) may change over the time. I will try to keep it updated.

Also I have not mentioned few other stuffs that are present in this subsystem or will be up-coming like:

  • Commitable sources - There is / was an interface / implementation where you can have commitable configuration sources. This is a way to commit or write new key - value configuration Set by the application back to the source. INI / JSON / XML are commitable sources.
  • There exists GetSubKey and GetSubKeys in IConfiguration where you can get collection of all keys that starts with parent key as prefix.
  • I see a ConfigurationFocus class recently added to beta-3 which can be used to filter configurations and create a separate instance of Configuration that contains all configuration with a particular PREFIX.

And finally, big thanks to you for patiently reading through all this.

Comments open for suggestion, critic, typo, improvements, changes or anything you think is appropriate when you said it to yourself out loud twice ;)

As always, happy coding!!