A Comprehensive Guide to Implementing Custom Configuration Sections with ConfigurationElementCollection in C#

Dec 05, 2025 · Programming · 9 views · 7.8

Keywords: C# | Configuration Section | ConfigurationElementCollection

Abstract: This article provides a detailed explanation of how to implement custom configuration sections in C# applications, particularly those involving configuration element collections. By analyzing common errors and best practices, it step-by-step covers the process from defining configuration elements, creating collection classes, to implementing configuration section handlers. Based on the best answer from the Q&A data, the article offers clear code examples and configuration instructions to help developers avoid pitfalls such as using the deprecated IConfigurationSectionHandler interface. Additionally, it explores configuration validation, enum type handling, and generic collection implementations, providing extended insights for complex configuration needs.

Introduction

In C# application development, configuration files are crucial for managing application settings. While the .NET framework provides standard sections like appSettings, complex scenarios often require custom configuration structures. This article delves into a common issue: implementing custom configuration sections with configuration element collections. The user initially attempted to use the deprecated IConfigurationSectionHandler interface, leading to exceptions and confusion. By analyzing the best answer, we demonstrate modern approaches and supplement with useful techniques from other answers.

Basic Structure of Configuration Sections

The core of a custom configuration section is inheriting from the ConfigurationSection class. In the Q&A, the user aimed to define a ServicesSection containing a Services collection. First, the App.config file must be correctly configured. The original configuration had an error: the line <ServicesSection type="RT.Core.Config.ServicesSection, RT.Core"> was unnecessary, as the type should be declared in configSections. The corrected configuration is:

<?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>

Here, the type attribute points to the custom ServiceConfigurationSection class, not an outdated handler. Collection elements use the <add> tag, which is the standard naming convention for configuration collections.

Implementing Configuration Elements

Configuration elements represent individual items in a collection, inheriting from ConfigurationElement. In the example, the ServiceConfig class defines two properties: Port and ReportType. The [ConfigurationProperty] attribute maps XML attributes. Key points include:

Code example:

public class ServiceConfig : ConfigurationElement
{
    [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; }
    }
}

Note that constructors are not required, as the configuration system creates instances via reflection.

Creating Configuration Element Collections

Collection classes inherit from ConfigurationElementCollection and manage multiple configuration elements. In the ServiceCollection class, two key methods must be overridden:

Additionally, indexers and other methods (e.g., Add, Remove) can be implemented for ease of use. Example code:

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

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

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

The XML tag names for the collection (e.g., <add>) are defined via attributes in the configuration section class.

Implementing the Configuration Section Class

The configuration section class inherits from ConfigurationSection and exposes collection properties. Use the [ConfigurationProperty] and [ConfigurationCollection] attributes to define collection behavior. In the best answer, the ServiceConfigurationSection class is:

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"];
        }
    }
}

AddItemName, ClearItemsName, and RemoveItemName specify the tag names for collection operations in XML, enhancing flexibility and readability.

Using the Configuration Section

In the application, access the configuration section via the ConfigurationManager.GetSection method. Example code:

ServiceConfigurationSection serviceConfigSection = 
    ConfigurationManager.GetSection("ServicesSection") as ServiceConfigurationSection;

if (serviceConfigSection != null)
{
    ServiceConfig firstService = serviceConfigSection.Services[0];
    Console.WriteLine($"Port: {firstService.Port}, ReportType: {firstService.ReportType}");
}

This avoids using the deprecated IConfigurationSectionHandler interface directly, ensuring modern and compatible code.

Extensions and Advanced Topics

Based on other answers, we can further optimize configuration implementation. For example, Answer 2 demonstrates handling enum types and complex nested structures. In LaneConfigElement, using enum properties provides automatic validation:

public enum Direction { Entry, Exit }

[ConfigurationProperty("Direction")]
public Direction? Direction
{
    get
    {
        return base["Direction"] as Direction?;
    }
}

If the XML contains undefined enum values, the system throws a ConfigurationErrorsException, offering built-in validation.

Answer 3 proposes a generic collection implementation for repetitive patterns. By creating GenericConfigurationElementCollection<T>, code duplication can be reduced:

public class GenericConfigurationElementCollection<T> : ConfigurationElementCollection, IEnumerable<T> where T : ConfigurationElement, new()
{
    protected override ConfigurationElement CreateNewElement()
    {
        return new T();
    }

    protected override object GetElementKey(ConfigurationElement element)
    {
        // Implement key logic based on element properties
        return element;
    }

    public new IEnumerator<T> GetEnumerator()
    {
        foreach (T element in this)
        {
            yield return element;
        }
    }
}

This improves maintainability but requires customization of key logic.

Common Errors and Debugging Tips

When implementing custom configuration sections, common errors include:

For debugging, check exception messages, which often indicate specific reasons for configuration loading failures, such as missing properties or type errors.

Conclusion

Implementing custom configuration sections with configuration element collections is an effective way to manage complex configurations in C# applications. By inheriting from ConfigurationSection, ConfigurationElement, and ConfigurationElementCollection, developers can create flexible and type-safe configuration structures. Based on the best answer, this article provides a comprehensive guide from basic implementation to advanced extensions, helping avoid common pitfalls and improving configuration code quality. In real-world projects, combining techniques like enum validation and generic collections can further optimize the maintainability and scalability of configuration systems.

Copyright Notice: All rights in this article are reserved by the operators of DevGex. Reasonable sharing and citation are welcome; any reproduction, excerpting, or re-publication without prior permission is prohibited.