Search code examples
xmlconfiguration-files

C# XML config file custom section always null


Working on a VSTO Outlook addin. I need to store configuration parameters in an XML file. I am struggling with some basic config file loading issue. I would appreciate a fresh view:

customConfiguration.xml

<?xml version="1.0" encoding="utf-8"?>
<configuration>

  <configSections>
      <section name="CDSSettings"  type="System.Configuration.NameValueSectionHandler" />
  </configSections>

  <CDSSettings>
    <add key="APIusername" value="myUser" />
    <add key="APIpassword" value="myPassword" />
  </CDSSettings>

    <appSettings>
      <add key="logLevel" value="0" /> 
  </appSettings>
</configuration>

Code

ExeConfigurationFileMap map = new ExeConfigurationFileMap();
map.ExeConfigFilename = "customConfiguration.xml";
Configuration config = ConfigurationManager.OpenMappedExeConfiguration(map, ConfigurationUserLevel.None);

AppSettingsSection appSettingsSection = (config.GetSection("appSettings") as AppSettingsSection);
// --> All ok

ConfigurationSection CDSSettings = (ConfigurationSection)config.GetSection("CDSSettings");
// --> How to get the APIusername key?

Any chance I can avoid an XML parser or SectionInformation.GetRawXml()?


Solution

  • Can't explain why ConfigurationManager.GetSection and config.GetSection returns different results object, that can be converted to NameValueCollection in first case, and section that is DefaultSection in second.

    I would suggest to create a custom section and use it:

    public class CDSSettings : ConfigurationSection
    {
        [ConfigurationProperty("MyValues")]
        public KeyValueConfigurationCollection MyValues
        {
            get { return (KeyValueConfigurationCollection) this["MyValues"]; }
            set { this["MyValues"] = value; }
        }
    }
    

    and config will looks like

    <section name="CDSSettings"  type="UCAddin.CDSSettings, UCAddin" />
    ...
    <CDSSettings>
      <MyValues>
        <add key="APIusername" value="myUser" />
        <add key="APIpassword" value="myPassword" />
      </MyValues>
    </CDSSettings>
    

    Retrieve code:

    var CDSSettings = (CDSSettings)config.GetSection("CDSSettings");
    

    More In case of custom section you can also specify different types of fields e.g. you can have separate named element:

    public class Credentials : ConfigurationElement
    {
        [ConfigurationProperty("login")]
        public string Login
        {
            get { return (string)this["login"]; }
            set { this["login"] = value; }
        }
    }
    

    with named proerties

    [ConfigurationProperty("credentials")]
    public Credentials Credentials
    {
        get { return (Credentials) this["credentials"]; }
        set { this["credentials"] = value; }
    }
    

    and config will looks like

    <CDSSettings>
      <credentials login="testlogin" />
    </CDSSettings>
    

    Check this MSDN article for more possibilities

    As AppSettings You can register property as default collection

    public class CDSSettings : ConfigurationSection
    {
    
        [ConfigurationProperty("", IsDefaultCollection = true)]
        public KeyValueConfigurationCollection MyValues => 
                    (KeyValueConfigurationCollection) this[string.Empty];
    }
    

    and have parameters as in App Settings

    <CDSSettings>
      <add key="login" value="User" />
    </CDSSettings>
    

    But in code those data will be accessible from the property (if you'll not implement indexer in the class)

    var settings = (CDSSettings)config.GetSection("CDSSettings");
    settings.MyValues