[c#] How to implement a ConfigurationSection with a ConfigurationElementCollection

If you are looking for a custom configuration section like following

<CustomApplicationConfig>
        <Credentials Username="itsme" Password="mypassword"/>
        <PrimaryAgent Address="10.5.64.26" Port="3560"/>
        <SecondaryAgent Address="10.5.64.7" Port="3570"/>
        <Site Id="123" />
        <Lanes>
          <Lane Id="1" PointId="north" Direction="Entry"/>
          <Lane Id="2" PointId="south" Direction="Exit"/>
        </Lanes> 
</CustomApplicationConfig>

then you can use my implementation of configuration section so to get started add System.Configuration assembly reference to your project

Look at the each nested elements I used, First one is Credentials with two attributes so lets add it first

Credentials Element

public class CredentialsConfigElement : System.Configuration.ConfigurationElement
    {
        [ConfigurationProperty("Username")]
        public string Username
        {
            get 
            {
                return base["Username"] as string;
            }
        }

        [ConfigurationProperty("Password")]
        public string Password
        {
            get
            {
                return base["Password"] as string;
            }
        }
    }

PrimaryAgent and SecondaryAgent

Both has the same attributes and seem like a Address to a set of servers for a primary and a failover, so you just need to create one element class for both of those like following

public class ServerInfoConfigElement : ConfigurationElement
    {
        [ConfigurationProperty("Address")]
        public string Address
        {
            get
            {
                return base["Address"] as string;
            }
        }

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

I'll explain how to use two different element with one class later in this post, let us skip the SiteId as there is no difference in it. You just have to create one class same as above with one property only. let us see how to implement Lanes collection

it is splitted in two parts first you have to create an element implementation class then you have to create collection element class

LaneConfigElement

public class LaneConfigElement : ConfigurationElement
    {
        [ConfigurationProperty("Id")]
        public string Id
        {
            get
            {
                return base["Id"] as string;
            }
        }

        [ConfigurationProperty("PointId")]
        public string PointId
        {
            get
            {
                return base["PointId"] as string;
            }
        }

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

    public enum Direction
    { 
        Entry,
        Exit
    }

you can notice that one attribute of LanElement is an Enumeration and if you try to use any other value in configuration which is not defined in Enumeration application will throw an System.Configuration.ConfigurationErrorsException on startup. Ok lets move on to Collection Definition

[ConfigurationCollection(typeof(LaneConfigElement), AddItemName = "Lane", CollectionType = ConfigurationElementCollectionType.BasicMap)]
    public class LaneConfigCollection : ConfigurationElementCollection
    {
        public LaneConfigElement this[int index]
        {
            get { return (LaneConfigElement)BaseGet(index); }
            set
            {
                if (BaseGet(index) != null)
                {
                    BaseRemoveAt(index);
                }
                BaseAdd(index, value);
            }
        }

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

        public void Clear()
        {
            BaseClear();
        }

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

        protected override object GetElementKey(ConfigurationElement element)
        {
            return ((LaneConfigElement)element).Id;
        }

        public void Remove(LaneConfigElement serviceConfig)
        {
            BaseRemove(serviceConfig.Id);
        }

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

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

    }

you can notice that I have set the AddItemName = "Lane" you can choose whatever you like for your collection entry item, i prefer to use "add" the default one but i changed it just for the sake of this post.

Now all of our nested Elements have been implemented now we should aggregate all of those in a class which has to implement System.Configuration.ConfigurationSection

CustomApplicationConfigSection

public class CustomApplicationConfigSection : System.Configuration.ConfigurationSection
    {
        private static readonly ILog log = LogManager.GetLogger(typeof(CustomApplicationConfigSection));
        public const string SECTION_NAME = "CustomApplicationConfig";

        [ConfigurationProperty("Credentials")]
        public CredentialsConfigElement Credentials
        {
            get
            {
                return base["Credentials"] as CredentialsConfigElement;
            }
        }

        [ConfigurationProperty("PrimaryAgent")]
        public ServerInfoConfigElement PrimaryAgent
        {
            get
            {
                return base["PrimaryAgent"] as ServerInfoConfigElement;
            }
        }

        [ConfigurationProperty("SecondaryAgent")]
        public ServerInfoConfigElement SecondaryAgent
        {
            get
            {
                return base["SecondaryAgent"] as ServerInfoConfigElement;
            }
        }

        [ConfigurationProperty("Site")]
        public SiteConfigElement Site
        {
            get
            {
                return base["Site"] as SiteConfigElement;
            }
        }

        [ConfigurationProperty("Lanes")]
        public LaneConfigCollection Lanes
        {
            get { return base["Lanes"] as LaneConfigCollection; }
        }
    }

Now you can see that we have two properties with name PrimaryAgent and SecondaryAgent both have the same type now you can easily understand why we had only one implementation class against these two element.

Before you can use this newly invented configuration section in your app.config (or web.config) you just need to tell you application that you have invented your own configuration section and give it some respect, to do so you have to add following lines in app.config (may be right after start of root tag).

<configSections>
    <section name="CustomApplicationConfig" type="MyNameSpace.CustomApplicationConfigSection, MyAssemblyName" />
  </configSections>

NOTE: MyAssemblyName should be without .dll e.g. if you assembly file name is myDll.dll then use myDll instead of myDll.dll

to retrieve this configuration use following line of code any where in your application

CustomApplicationConfigSection config = System.Configuration.ConfigurationManager.GetSection(CustomApplicationConfigSection.SECTION_NAME) as CustomApplicationConfigSection;

I hope above post would help you to get started with a bit complicated kind of custom config sections.

Happy Coding :)

****Edit**** To Enable LINQ on LaneConfigCollection you have to implement IEnumerable<LaneConfigElement>

And Add following implementation of GetEnumerator

public new IEnumerator<LaneConfigElement> GetEnumerator()
        {
            int count = base.Count;
            for (int i = 0; i < count; i++)
            {
                yield return base.BaseGet(i) as LaneConfigElement;
            }
        }

for the people who are still confused about how yield really works read this nice article

Two key points taken from above article are

it doesn’t really end the method’s execution. yield return pauses the method execution and the next time you call it (for the next enumeration value), the method will continue to execute from the last yield return call. It sounds a bit confusing I think… (Shay Friedman)

Yield is not a feature of the .Net runtime. It is just a C# language feature which gets compiled into simple IL code by the C# compiler. (Lars Corneliussen)

Examples related to c#

How can I convert this one line of ActionScript to C#? Microsoft Advertising SDK doesn't deliverer ads How to use a global array in C#? How to correctly write async method? C# - insert values from file into two arrays Uploading into folder in FTP? Are these methods thread safe? dotnet ef not found in .NET Core 3 HTTP Error 500.30 - ANCM In-Process Start Failure Best way to "push" into C# array

Examples related to configuration

My eclipse won't open, i download the bundle pack it keeps saying error log Getting value from appsettings.json in .net core pgadmin4 : postgresql application server could not be contacted. ASP.NET Core configuration for .NET Core console application Turning off eslint rule for a specific file PHP Warning: Module already loaded in Unknown on line 0 How to read AppSettings values from a .json file in ASP.NET Core How to store Configuration file and read it using React Hadoop cluster setup - java.net.ConnectException: Connection refused Maven Jacoco Configuration - Exclude classes/packages from report not working

Examples related to app-config

Is ConfigurationManager.AppSettings available in .NET Core 2.0? How to read AppSettings values from a .json file in ASP.NET Core What is App.config in C#.NET? How to use it? How to make spring inject value into a static field App.Config change value Get connection string from App.config How should I edit an Entity Framework connection string? Equivalent to 'app.config' for a library (DLL) How to implement a ConfigurationSection with a ConfigurationElementCollection App.Config Transformation for projects which are not Web Projects in Visual Studio?

Examples related to configuration-files

Is it .yaml or .yml? How to handle configuration in Go How to read a configuration file in Java Creating a config file in PHP Clean out Eclipse workspace metadata Adding and reading from a Config file Where is the user's Subversion config file stored on the major operating systems? How do I find out which settings.xml file maven is using What's in an Eclipse .classpath/.project file? How to store Node.js deployment settings/configuration files?