I'm actually astonished at the amount of time i spent trying to fix this and got absolutely nowhere in the end. I have the following app.config file i'm trying to read:

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <configSections>
    <section name="BattleCity.Configuration" type="BattleCity.Configuration.GameConfiguration, BattleCity"/>
  </configSections>

  <BattleCity.Configuration>
    <environment>
      <add blockType="Test1"/>
      <add blockType="Test2"/>
    </environment>
  </BattleCity.Configuration>
</configuration>

And here is the code for it:

GameConfiguration.cs

#region Using Directives

using System;
using System.Configuration;

#endregion

namespace BattleCity.Configuration
{
    public class GameConfiguration : ConfigurationSection
    {
        #region Public Members

        /// <summary>
        /// Retrieves an instance of the <see cref="GameConfiguration"/> with the
        /// values populated from the App.config file. 
        /// </summary>
        /// <returns>An <see cref="GameConfiguration" /> instance.</returns>
        public static GameConfiguration GetConfig()
        {
            return ConfigurationManager.GetSection("BattleCity.Configuration") as GameConfiguration;
        }

        /// <summary>
        /// A <see cref="GameConfigurationDimensions"/> instance. 
        /// </summary>
        [ConfigurationProperty("dimensions", IsRequired = true)]
        public GameConfigurationDimensions Dimensions
        {
            get
            {
                return this["dimensions"] as GameConfigurationDimensions;
            }
        }

        /// <summary>
        /// A <see cref="GameConfigurationElementCollection"/> instance that provides access 
        /// to a set of <see cref="GameConfigurationEnvironment"/>s.
        /// </summary>
        [ConfigurationProperty("environment", IsDefaultCollection = true, IsRequired = true)]
        public GameConfigurationElementCollection<GameConfigurationEnvironment> Environment
        {
            get
            {
                return this["environment"] as GameConfigurationElementCollection<GameConfigurationEnvironment>;
            }
        }

        #endregion
    }
}

GameConfigurationDimensions.cs

#region Using Directives

using System;
using System.Configuration;

#endregion

namespace BattleCity.Configuration
{
    public class GameConfigurationDimensions : ConfigurationElement
    {
        public GameConfigurationDimensions()
        { }

        #region Properties

        /// <summary>
        /// Gets map height value.
        /// </summary>
        [ConfigurationProperty("mapHeight", IsRequired = true)]
        public int MapHeight
        {
            get
            {
                return (int)this["mapHeight"];
            }
        }

        /// <summary>
        /// Gets map width value.
        /// </summary>
        [ConfigurationProperty("mapWidth", IsRequired = true)]
        public int MapWidth
        {
            get
            {
                return (int)this["mapWidth"];
            }
        }

        /// <summary>
        /// Gets block pixel size value.
        /// </summary>
        [ConfigurationProperty("blockPixelSize", IsRequired = true)]
        public int BlockPixelSize
        {
            get
            {
                return (int)this["blockPixelSize"];
            }
        }

        /// <summary>
        /// Gets number of blocks per row.
        /// </summary>
        [ConfigurationProperty("blocksPerRow", IsRequired = true)]
        public int BlocksPerRow
        {
            get
            {
                return (int)this["blocksPerRow"];
            }
        }

        #endregion
    }
}

GameConfigurationElementCollection.cs

#region Using Directives

using System;
using System.Configuration;
using System.Collections.Generic;

#endregion

namespace BattleCity.Configuration
{
    /// <summary>
    /// Defines generic configuration element collection of the App.config file.
    /// </summary>
    /// <typeparam name="TYPE">Collection element type, which is either 
    /// <see cref="GameConfigurationDimensions"/> or 
    /// <see cref="GameConfigurationEnvironment"/>
    /// </typeparam>
    [Serializable()]
    public class GameConfigurationElementCollection<TYPE> : ConfigurationElementCollection, IEnumerable<TYPE> where TYPE : ConfigurationElement, new()
    {
        #region Protected Members

        /// <summary>
        /// See <see cref="ConfigurationElementCollection.CreateNewElement()"/> for more information.
        /// </summary>
        protected override ConfigurationElement CreateNewElement()
        {
            return new TYPE();
        }

        /// <summary>
        /// See <see cref="ConfigurationElementCollection.GetElementKey"/> for more information.
        /// </summary>
        protected override object GetElementKey(ConfigurationElement element)
        {
            return ((TYPE)element).GetType();
        }

        #endregion

        #region Public Members

        /// <summary>
        /// See <see cref="IEnumerable{T}.GetEnumerator"/> for more information.
        /// </summary>
        public new IEnumerator<TYPE> GetEnumerator()
        {
            int count = base.Count;

            for (int i = 0; i < count; i++)
            {
                yield return (TYPE)base.BaseGet(i);
            }
        }

        /// <summary>
        /// Adds a new element to the collection.
        /// </summary>
        /// <param name="element">The element to be added.</param>
        public void Add(TYPE element)
        {
            base.BaseAdd(element);
        }

        #endregion

        #region Indexers

        /// <summary>
        /// Gets or sets a configuration element of generic type at a specified ordinal index.
        /// </summary>
        public TYPE this[int index]
        {
            get
            {
                return base.BaseGet(index) as TYPE;
            }
            set
            {
                if (base.BaseGet(index) != null)
                {
                    base.BaseRemoveAt(index);
                }

                this.BaseAdd(index, value);
            }
        }

        #endregion
    }
}

GameConfigurationEnvironment.cs

#region Using Directives

using System;
using System.Configuration;

#endregion

namespace BattleCity.Configuration
{
    public class GameConfigurationEnvironment : ConfigurationElement
    {
        #region Properties

        /// <summary>
        /// Gets block type value.
        /// </summary>
        [ConfigurationProperty("blockType", IsRequired = true)]
        public string BlockType
        {
            get
            {
                return (string)this["blockType"];
            }
        }

        #endregion
    }
}

The exception i am getting is

The entry 'BattleCity.Configuration.GameConfigurationEnvironment' has already been added.

which happens when it tries to read the second <add> tag. Could someone PLEASE tell me what the heck i'm doing wrong before i shoot myself over this.

Thank you.

Well, it turns out that the problem is somewhere in the generic element collection class. Not quite sure where exactly. I just recoded the class and removed generics and it worked okay. I'll make an update once i figure out what was causing the issue.

Looks like you can't use generics with ConfigurationElementCollection. Probably why it's not provided by default. So i went back to creating a collection for each element. Makes no sense, but at least it works.

The function GetElementKey should retrun a unique value. Change it to return some unique value.
You may use blockType values if they are unique
"return ((GameConfigurationEnvironment)element).BlockType;"
instead of
"return ((TYPE)element).GetType()"

Be a part of the DaniWeb community

We're a friendly, industry-focused community of developers, IT pros, digital marketers, and technology enthusiasts meeting, networking, learning, and sharing knowledge.