A simple approach to custom configuration sections in C#

App.config is a great convenient place to store the sorts of magic values that your application needs in order to run, but which you might want to change after installing, without recompiling. Using the built-in Properties\Settings.settings UI, and then accessing those with Properties.Settings.Default, is a fine way of putting custom data in App.config.

However, it does start to struggle when you want to store more complex data: arrays, objects, etc. Sure, it can technically handle custom object (and it will use XmlSerializer to deserialize them), but different versions of Visual Studio will serialize them in slightly different ways (regarding whitespace, etc), meaning that you’ll end up being constantly prompted to synchronize the values stored in App.config, Settings.settings, and Settings.Designer.cs.

Thankfully it’s possible to create your own custom configuration sections , but the suggested approach uses a very boilerplate-y combination of ConfigurationSections, ConfigurationElements, ConfigurationProperties, and other nonsense. I just wanted to have a simple class, which uses XmlSerializer to deserialize.

Thankfully this is possible, too, if you dig hard enough. This is my approach:

public class XmlConfigurationSection : ConfigurationSection
{
    // This may be fetched multiple times: XmlReaders can't be reused, so load it into an XDocument instead
    private XDocument document;

    protected override void DeserializeSection(XmlReader reader)
    {
        this.document = XDocument.Load(reader);
    }

    protected override object GetRuntimeObject()
    {
        // This is cached by ConfigurationManager, so no point in duplicating it to stop other people from modifying it
        return this.document;
    }
}

public class AppSettings
{
    private const string sectionName = "settings";
    private static readonly XmlSerializer serializer = new XmlSerializer(typeof(AppSettings), new XmlRootAttribute(sectionName));

    public static readonly AppSettings Instance;

    static AppSettings()
    {
        var document = (XDocument)ConfigurationManager.GetSection(sectionName);
        Instance = (AppSettings)serializer.Deserialize(document.CreateReader());
    }

    // Add your custom fields in here....

    public string SomeProperty { get; set; } = "Some Default Value";

    [XmlElement("Value")]
    public List<int> SomeList { get; set; } = new List<int>() { 1, 2, 3 };

    public override string ToString()
    {
        using (var writer = new StringWriter())
        {
            serializer.Serialize(writer, this);
            return writer.ToString();
        }
    }
}

Then in your App.config:

<?xml version="1.0" encoding="utf-8"?>
<configuration>
	<configSections>
    	<section name="settings" type="MyRootNamespace.MyNamespace.XmlConfigurationSection, MyAssembly"/>
    </configSections>

    <settings>
    	<SomeProperty>Some Default Value</SomeProperty>
    	<SomeList>
    		<Value>1</Value>
    		<Value>2</Value>
    		<Value>3</Value>
    	</SomeList>
    </settings>
</configuration>

Use it by accessing e.g. AppSettings.Instance.SomeProperty.

Adjust MyRootNamespace.MyNamespace.XmlConfigurationSection to the fully-qualified name of your XmlConfigurationSection class, and MyAssembly to the name of the assembly containing XmlConfigurationSection. If you want to change the name of the section from “settings” to something else, you’ll also need to change it in <section name="settings", and in AppSettings.sectionName.

I’ve overridden AppSettings.ToString() to display the serialized version of AppSettings, so that you can evaluate AppSettings.Instance.ToString() in the Watch / Immediate window, and get a default value to paste into App.Config.