Saturday, July 25, 2015

Cascading AppSettings from Multiple Config Files

In my previous blog post I talked about creating complex configuration objects from AppSettings. I really like this practice, but it can cause your config files to grow pretty large. One solution is to break your app.config into multiple files using the SectionInformation.ConfigSource property.

This also has the added side effect of allowing you to include defaults and overrides by having those settings cascade. I have created an extension method to combine a series of NameValueCollections, such as an AppSettings section. You can also grab this code from the GitHub project.

Combine Extension

public static NameValueCollection Combine(
    this NameValueCollection collection, 
    params NameValueCollection[] collections)
{
    var result = new NameValueCollection { collection };
 
    foreach (var subCollection in collections)
        foreach (var key in subCollection.AllKeys)
        {
            if (result.AllKeys.Contains(key))
                continue;
 
            var value = subCollection[key];
            result.Add(key, value);
        }
 
    return result;
}

Sample App.config

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
 
  <configSections>
    <section name="primaryValues" type="System.Configuration.NameValueFileSectionHandler, System, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"/>
    <section name="defaultValues" type="System.Configuration.NameValueFileSectionHandler, System, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"/>
  </configSections>
 
  <primaryValues>
    <add key="TestConfig.String" value="Moon"/>
  </primaryValues>
 
  <defaultValues configSource="Default.config" />
 
</configuration>

Sample Default.config

<?xml version="1.0" encoding="utf-8" ?>
<defaultValues>
  <add key="TestConfig.Int" value="42"/>
  <add key="TestConfig.String" value="Goodnight"/>
</defaultValues>

Unit Test

[Fact]
public void CombineAndCreateObject()
{
    var primaryValues = (NameValueCollection) ConfigurationManager
        .GetSection("primaryValues");
 
    var primaryObject = primaryValues.CreateObject<TestConfig>();
    Assert.Equal("Moon", primaryObject.String);
    Assert.Equal(0, primaryObject.Int);
 
    var defaultValues = (NameValueCollection)ConfigurationManager
        .GetSection("defaultValues");
 
    var defaultObject = defaultValues.CreateObject<TestConfig>();
    Assert.Equal("Goodnight", defaultObject.String);
    Assert.Equal(42, defaultObject.Int);
 
    var combineValues = primaryValues.Combine(defaultValues);
 
    var combineObject = combineValues.CreateObject<TestConfig>();
    Assert.Equal("Moon", combineObject.String);
    Assert.Equal(42, combineObject.Int);
}

Enjoy,
Tom

No comments:

Post a Comment

Real Time Web Analytics