Search code examples
c#configurationapp-configconfiguration-files

How to implement a ConfigurationSection with a ConfigurationElementCollection


I am trying to implement a custom configuration section in a project and I keep running up against exceptions that I do not understand. I am hoping someone can fill in the blanks here.

I have App.config that looks like this:

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
    <configSections>
        <section name="ServicesSection" type="RT.Core.Config.ServicesConfigurationSectionHandler, RT.Core"/>
    </configSections>
    <ServicesSection type="RT.Core.Config.ServicesSection, RT.Core">
            <Services>
                <AddService Port="6996" ReportType="File" />
                <AddService Port="7001" ReportType="Other" />
            </Services>
        </ServicesSection>
</configuration>

I have a ServiceConfig element defined like so:

public class ServiceConfig : ConfigurationElement
  {
    public ServiceConfig() {}

    public ServiceConfig(int port, string reportType)
    {
      Port = port;
      ReportType = reportType;
    }

    [ConfigurationProperty("Port", DefaultValue = 0, IsRequired = true, IsKey = true)]
    public int Port 
    {
      get { return (int) this["Port"]; }
      set { this["Port"] = value; }
    }

    [ConfigurationProperty("ReportType", DefaultValue = "File", IsRequired = true, IsKey = false)]
    public string ReportType
    {
      get { return (string) this["ReportType"]; }
      set { this["ReportType"] = value; }
    }
  }

And I have a ServiceCollection defined like so:

public class ServiceCollection : ConfigurationElementCollection
  {
    public ServiceCollection()
    {
      Console.WriteLine("ServiceCollection Constructor");
    }

    public ServiceConfig this[int index]
    {
      get { return (ServiceConfig)BaseGet(index); }
      set
      {
        if (BaseGet(index) != null)
        {
          BaseRemoveAt(index);
        }
        BaseAdd(index, value);
      }
    }

    public void Add(ServiceConfig serviceConfig)
    {
      BaseAdd(serviceConfig);
    }

    public void Clear()
    {
      BaseClear();
    }

    protected override ConfigurationElement CreateNewElement()
    {
      return new ServiceConfig();
    }

    protected override object GetElementKey(ConfigurationElement element)
    {
      return ((ServiceConfig) element).Port;
    }

    public void Remove(ServiceConfig serviceConfig)
    {
      BaseRemove(serviceConfig.Port);
    }

    public void RemoveAt(int index)
    {
      BaseRemoveAt(index);
    }

    public void Remove(string name)
    {
      BaseRemove(name);
    }
  }

The part I am missing is what to do for the handler. Originally, I tried to implement an IConfigurationSectionHandler but found two things:

  1. it didn't work
  2. it's deprecated.

I'm completely lost now on what to do so I can read my data from config. Any help please!


Solution

  • The previous answer is correct but I'll give you all the code as well.

    Your app.config should look like this:

    <?xml version="1.0" encoding="utf-8" ?>
    <configuration>
       <configSections>
          <section name="ServicesSection" type="RT.Core.Config.ServiceConfigurationSection, RT.Core"/>
       </configSections>
       <ServicesSection>
          <Services>
             <add Port="6996" ReportType="File" />
             <add Port="7001" ReportType="Other" />
          </Services>
       </ServicesSection>
    </configuration>
    

    Your ServiceConfig and ServiceCollection classes remain unchanged.

    You need a new class:

    public class ServiceConfigurationSection : ConfigurationSection
    {
       [ConfigurationProperty("Services", IsDefaultCollection = false)]
       [ConfigurationCollection(typeof(ServiceCollection),
           AddItemName = "add",
           ClearItemsName = "clear",
           RemoveItemName = "remove")]
       public ServiceCollection Services
       {
          get
          {
             return (ServiceCollection)base["Services"];
          }
       }
    }
    

    And that should do the trick. To consume it you can use:

    ServiceConfigurationSection serviceConfigSection =
       ConfigurationManager.GetSection("ServicesSection") as ServiceConfigurationSection;
    
    ServiceConfig serviceConfig = serviceConfigSection.Services[0];