I followed a tutorial on Stackoverflow to add a custom configuration section to my exe's config file, however on calling it, it is returning null. It's not even getting into the static constructors so something is clearly wrong, but I can't see what.
Here is my config file and the section I wish to find.
<configuration>
<appSettings>
</appSettings>
<configSections>
<section name="PresetFilters" type="ImageTool.PresetFiltersConfiguration, ImageTool" />
</configSections>
<PresetFilters>
<add key="Default,-20,0,0,0,0" />
<add key="No Change,0,0,0,0,0" />
<add key="Dark Photo,10,10,0,0,-10" />
</PresetFilters>
</configuration>
I call it like this:
PresetFiltersConfiguration pf = (PresetFiltersConfiguration)ConfigurationManager.GetSection("PresetFilters");
and it returns null and doesn't even enter my class or class statics. Here's the code. Any help would be appreciated. Thanks.
public class PresetFiltersConfiguration : ConfigurationSection
{
private static ConfigurationPropertyCollection properties;
private static ConfigurationProperty propPresets;
static PresetFiltersConfiguration()
{
propPresets = new ConfigurationProperty(null, typeof(PresetFiltersElementCollection),
null,
ConfigurationPropertyOptions.IsDefaultCollection);
properties = new ConfigurationPropertyCollection { propPresets };
}
protected override ConfigurationPropertyCollection Properties
{
get
{
return properties;
}
}
public PresetFiltersElementCollection PresetFilter
{
get
{
return this[propPresets] as PresetFiltersElementCollection;
}
}
}
public class PresetFiltersElementCollection : ConfigurationElementCollection
{
public PresetFiltersElementCollection()
{
properties = new ConfigurationPropertyCollection();
}
private static ConfigurationPropertyCollection properties;
protected override ConfigurationPropertyCollection Properties
{
get
{
return properties;
}
}
public override ConfigurationElementCollectionType CollectionType
{
get
{
return ConfigurationElementCollectionType.BasicMap;
}
}
protected override string ElementName
{
get
{
return "add";
}
}
protected override ConfigurationElement CreateNewElement()
{
return new PresetFiltersElement();
}
protected override object GetElementKey(ConfigurationElement element)
{
var elm = element as PresetFiltersElement;
if (elm == null) throw new ArgumentNullException();
return elm.KeyName;
}
}
public class PresetFiltersElement : ConfigurationElement
{
private static ConfigurationPropertyCollection properties;
private static ConfigurationProperty propKey;
protected override ConfigurationPropertyCollection Properties
{
get
{
return properties;
}
}
public PresetFiltersElement()
{
propKey = new ConfigurationProperty("key", typeof(string),
null,
ConfigurationPropertyOptions.IsKey);
properties = new ConfigurationPropertyCollection { propKey };
}
public PresetFiltersElement(string keyName)
: this()
{
KeyName = keyName;
}
public string KeyName
{
get
{
return this[propKey] as string;
}
set
{
this[propKey] = value;
}
}
}
You need something like this in your app.config, otherwise the application won't know how to process your new section.
<configSections>
<sectionGroup name="pageAppearanceGroup">
<section
name="PresetFilters"
type="PresetFiltersConfiguration"
allowLocation="true"
allowDefinition="Everywhere"
/>
</sectionGroup>
</configSections>
After applying the fixes it worked, but the actual fault is to do with it not working for class libraries, and I've created a new question for this.
Thanks.
Related
I am trying to create a custom section in my web.config. I have a custom class and a declaration section in my web.config.
when I run my code however I am getting this error "...does not inherit from System.Configuration.IConfigurationSectionHandler'."
MSDN documentation states that this Interface is now deprecated.
I don't understand what cold be happening.
FYI I used this (http://blogs.perficient.com/microsoft/2017/01/4-easy-steps-to-custom-sections-in-web-config/ as my guide to create my custom section.
Here is my web.config
<configuration>
<configSections>
<section name="nlog" type="NLog.Config.ConfigSectionHandler, NLog" />
<!-- For more information on Entity Framework configuration, visit http://go.microsoft.com/fwlink/?LinkID=237468 -->
<section name="entityFramework" type="System.Data.Entity.Internal.ConfigFile.EntityFrameworkSection, EntityFramework, Version=6.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" requirePermission="false" />
<section name ="BDKConfigs" type="SPS.CardDecryption.Services.BDKConfigs" allowLocation="true" allowDefinition="Everywhere" />
</configSections>
Here is my custom section in the web.config
<BDKConfigs>
<!--collection-->
<BDK>
<!--elements-->
<add name="IDTechPublicBDK" value="0123456789ABCDEFFEDCBA9876543210"/>
<add name="IDTechProdBDK" value=""/>
</BDK>
</BDKConfigs>
this is my custom class
using System.Configuration;
namespace myNameSpace
{
//This class reads the defined config section (if available) and stores it locally in the static _Config variable.
public class BDKConfigs
{
public static BDKConfigsSection _Config = ConfigurationManager.GetSection("BDKConfigs") as BDKConfigsSection;
public static BDKElementCollection GetBDKGroups()
{
return _Config.BDKConfigs;
}
}
public class BDKConfigsSection : ConfigurationSection
{
//Decorate the property with the tag for your collection.
[ConfigurationProperty("BDK")]
public BDKElementCollection BDKConfigs
{
get { return (BDKElementCollection)this["BDK"]; }
}
}
//Extend the ConfigurationElementCollection class.
//Decorate the class with the class that represents a single element in the collection.
[ConfigurationCollection(typeof(BDKElement))]
public class BDKElementCollection : ConfigurationElementCollection
{
public BDKElement this[int index]
{
get { return (BDKElement)BaseGet(index); }
set
{
if (BaseGet(index) != null)
BaseRemoveAt(index);
BaseAdd(index, value);
}
}
protected override ConfigurationElement CreateNewElement()
{
return new BDKElement();
}
protected override object GetElementKey(ConfigurationElement element)
{
return ((BDKElement)element).Name;
}
}
//Extend the ConfigurationElement class. This class represents a single element in the collection.
//Create a property for each xml attribute in your element.
//Decorate each property with the ConfigurationProperty decorator. See MSDN for all available options.
public class BDKElement : ConfigurationElement
{
[ConfigurationProperty("name", IsRequired = true)]
public string Name
{
get { return (string)this["name"]; }
set { this["name"] = value; }
}
[ConfigurationProperty("queryString", IsRequired = false)]
public string Value
{
get { return (string)this["Value"]; }
set { this["Value"] = value; }
}
}
}
The issue was that I had the wrong type specified in the web.config. When I changed my web.config to
type="SPS.CardDecryption.Services.BDKConfigsSection , worked like a charm.
I have updated answer:
public class BDKConfigsSection : ConfigurationSection
{
[ConfigurationProperty("BDK")]
public BDKElementCollection BDKConfigs
{
get { return (BDKElementCollection) base["BDK"]; }
}
}
[ConfigurationCollection(typeof(BDKElement))]
public class BDKElementCollection : ConfigurationElementCollection
{
public BDKElement this[int index]
{
get { return (BDKElement)BaseGet(index); }
set
{
if (BaseGet(index) != null)
BaseRemoveAt(index);
BaseAdd(index, value);
}
}
protected override ConfigurationElement CreateNewElement()
{
return new BDKElement();
}
protected override object GetElementKey(ConfigurationElement element)
{
return ((BDKElement)element).Name;
}
public override ConfigurationElementCollectionType CollectionType
{
get { return ConfigurationElementCollectionType.BasicMapAlternate; }
}
protected override string ElementName => "BDKItem";
}
public class BDKElement : ConfigurationElement
{
[ConfigurationProperty("name", IsRequired = true)]
public string Name
{
get { return (string)this["name"]; }
set { this["name"] = value; }
}
[ConfigurationProperty("queryString", IsRequired = false)]
public string Value
{
get { return (string)this["queryString"]; }
set { this["queryString"] = value; }
}
}
that one working fine :
<BDKConfigsSection>
<BDK>
<BDKItem name="IDTechPublicBDK" queryString="0123456789ABCDEFFEDCBA9876543210"/>
<BDKItem name="IDTechProdBDK" queryString=""/>
</BDK> </BDKConfigsSection>
I have a small app that searches my SQL Server database for the given value in all of the fields where it could be used. Part of that is a custom app.config section to hold all of these fields. However I'm getting an error on this line in the app:
var section = (DatabaseFieldSection)ConfigurationManager.GetSection("DatabaseFields");
app.config:
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<configSections>
<section name="DatabaseFields" type="PartyFinder.DataAccess.DatabaseFieldSection, DataAccess"/>
</configSections>
<connectionStrings>
<!-- Data removed for security purposes -->
</connectionStrings>
<DatabaseFields>
<add Key="Person.FirstName (SV)" TableName="dbo.Person" ColumnName="FirstName" FieldType="SV" />
</DatabaseFields>
<startup>
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5.2" />
</startup>
</configuration>
Classes:
using System;
using System.Configuration;
namespace PartyFinder.DataAccess
{
public class DatabaseFieldElement : ConfigurationElement
{
[ConfigurationProperty("Key", IsKey = true, IsRequired = true)]
public string Key
{
get { return (string)base["Key"]; }
}
[ConfigurationProperty("TableName", IsKey = false, IsRequired = true)]
public string TableName
{
get { return (string)base["TableName"]; }
}
[ConfigurationProperty("ColumnName", IsKey = false, IsRequired = true)]
public string ColumnName
{
get { return (string)base["ColumnName"]; }
}
[ConfigurationProperty("FieldType", IsKey = false, IsRequired = true)]
public string FieldType
{
get { return (string)base["FieldType"]; }
}
}
public class DatabaseFieldSection : ConfigurationSection
{
[ConfigurationProperty("DatabaseFields", IsDefaultCollection = true)]
[ConfigurationCollection(typeof(DatabaseFieldCollection), AddItemName = "add", ClearItemsName = "clear", RemoveItemName = "remove")]
public DatabaseFieldCollection DatabaseFields
{
get { return ((DatabaseFieldCollection)base["DatabaseFields"]); }
}
}
public class DatabaseFieldCollection : ConfigurationElementCollection
{
internal const string PropertyName = "Fields";
public override ConfigurationElementCollectionType CollectionType
{
get
{
return ConfigurationElementCollectionType.AddRemoveClearMap;
}
}
protected override string ElementName
{
get
{
return PropertyName;
}
}
protected override bool IsElementName(string elementName)
{
return elementName.Equals(PropertyName, StringComparison.InvariantCultureIgnoreCase);
}
public override bool IsReadOnly()
{
return false;
}
protected override ConfigurationElement CreateNewElement()
{
return new DatabaseFieldElement();
}
protected override object GetElementKey(ConfigurationElement element)
{
return ((DatabaseFieldElement)(element)).Key;
}
public DatabaseFieldElement this[int id]
{
get
{
return (DatabaseFieldElement)BaseGet(id);
}
}
}
}
I have done a ton of searches and nothing has worked so far. Any advice is much appreciated!
I managed to get the following to work in a console app by adding a parent DatabaseConfig node:
The DatabaseFieldSection class:
public class DatabaseFieldSection : ConfigurationSection
{
public static DatabaseFieldSection GetConfig()
{
return (DatabaseFieldSection)System.Configuration.ConfigurationManager.GetSection("DatabaseConfig") ?? new DatabaseFieldSection();
}
[System.Configuration.ConfigurationProperty("DatabaseFields")]
[ConfigurationCollection(typeof(DatabaseFieldCollection), AddItemName = "add")]
public DatabaseFieldCollection DatabaseFields
{
get
{
object o = this["DatabaseFields"];
return o as DatabaseFieldCollection;
}
}
}
And the App.config:
<configSections>
<section name="DatabaseConfig" type="TestEnvironment.DBConfig.DatabaseFieldSection, TestEnvironment"/>
</configSections>
<DatabaseConfig>
<DatabaseFields>
<add Key="Person.FirstName (SV)" TableName="dbo.Person" ColumnName="FirstName" FieldType="SV" />
</DatabaseFields>
</DatabaseConfig>
Usage:
var config = DatabaseFieldSection.GetConfig();
It seems as though the config <section /> cannot be the same as the config element collection name. e.g DatabaseFields, it needs to be:
Section > Collection > Items
Or:
DatabaseConfig > DatabaseFields > add
I have a custom config which is based on some classes. My problem is that I get an error saying that a config element is unrecognized. The class is as follows:
[ConfigurationCollection(typeof(SectionItem), AddItemName = "Item", CollectionType = ConfigurationElementCollectionType.BasicMap)]
public class Sections : ConfigurationElementCollection
{
public SectionItem this[int index]
{
get { return BaseGet(index) as SectionItem; }
set
{
if (BaseGet(index) != null)
{
BaseRemoveAt(index);
}
BaseAdd(index, value);
}
}
public new SectionItem this[string response]
{
get { return (SectionItem)BaseGet(response); }
set
{
if (BaseGet(response) != null)
{
BaseRemoveAt(BaseIndexOf(BaseGet(response)));
}
BaseAdd(value);
}
}
protected override ConfigurationElement CreateNewElement()
{
return new SectionItem();
}
protected override object GetElementKey(ConfigurationElement element)
{
return ((SectionItem)element).Key;
}
}
And the SectionItem class:
public class SectionItem : ConfigurationElement
{
[ConfigurationProperty("key", IsRequired = true, IsKey = true)]
public string Key
{
get { return this["key"] as string; }
}
}
If I add a configuration property of type SectionItems in the Sections class that won't work for me, because I want to have multiple SectonItems inside the Section tag in the config file. I've searched for solutions but everything I've found didn't do the trick with this.
For a better understanding of what I'm trying to achieve this is how my config looks:
<configuration>
<configSections>
<section name="AdminConfig" type="XmlTest.AdminConfig, XmlTest"/>
</configSections>
<startup>
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5" />
</startup>
<AdminConfig>
<Field name="field1" key="12345" path="asd"/>
<Section>
<Item key="12345"/>
<Item key="54321"/>
</Section>
</AdminConfig>
</configuration>
OK so I've found the problem. Although the Sections class had this [ConfigurationCollection(typeof(SectionItem), AddItemName = "Item", CollectionType = ConfigurationElementCollectionType.BasicMap)] I had to annotate the property in the ConfigurationSection class, as follows:
[ConfigurationProperty("Section")]
[ConfigurationCollection(typeof(Sections), AddItemName = "Item")]
public Sections Sections
{
get
{
return (Sections)this["Section"];
}
}
Now the items are recognized and stuff is working properly.
I want the best of both worlds: I want to be able to persist changes during runtime, like the User scoped application settings can, and I also want these settings to be global. Is there some way to accomplish this through app.config settings files? Should I look at some other way to persist global, runtime editable settings for my application?
The built in configuration manager in .Net which is used to deal with application settings in config files is read-only so technically, you can't do it using the built in libraries, however, the config file is just xml, so there's no reason why you can't just update the config file using the standard xml methods and then call
ConfigurationManager.RefreshSection("appSettings")
when you want to reload your settings
Furthermore, the OpenMappedExeConfiguration() Method of the ConfigurationManager lets you dynamically load a config file of your choosing (granted it follows the .NET xml config schema) and have your application load configuariton options from it, so you can modifcations to the file as #lomax indicated and have a common file that you can load from all your applications, using this same method.
Here's some info on OpenMappedExeConfiguration
Ok, this is how I solved it:
I created really basic ConfigurationSection, ConfigurationElement, and ConfigurationElementCollection implementations:
public class CoreConfigurationSection : ConfigurationSection
{
[ConfigurationProperty("settings", IsDefaultCollection = true)]
[ConfigurationCollection(typeof(CoreSettingCollection), AddItemName = "setting")]
public CoreSettingCollection Settings
{
get
{
return (CoreSettingCollection)base["settings"];
}
}
}
public class CoreSetting : ConfigurationElement
{
public CoreSetting() { }
public CoreSetting(string name, string value)
{
Name = name;
Value = value;
}
[ConfigurationProperty("name", IsRequired = true, IsKey = true)]
public string Name
{
get { return (string)this["name"]; }
set { this["name"] = value; }
}
[ConfigurationProperty("value", DefaultValue = null, IsRequired = true, IsKey = false)]
public string Value
{
get { return (string)this["value"]; }
set { this["value"] = value; }
}
}
public class CoreSettingCollection : ConfigurationElementCollection
{
public new string this[string name]
{
get { return BaseGet(name) == null ? string.Empty : ((CoreSetting)BaseGet(name)).Value; }
set { Remove(name); Add(name, value); }
}
public void Add(string name, string value)
{
if (!string.IsNullOrEmpty(value))
BaseAdd(new CoreSetting(name, value));
}
public void Remove(string name)
{
if (BaseGet(name) != null)
BaseRemove(name);
}
protected override ConfigurationElement CreateNewElement()
{
return new CoreSetting();
}
protected override object GetElementKey(ConfigurationElement element)
{
return ((CoreSetting)element).Name;
}
}
And then a class to manage the configuration file:
public static class Settings
{
private static string _root { get { return "core"; } }
private static Configuration Load()
{
string filename = Path.Combine(Core.BaseDirectory, "core.config");
var mapping = new ExeConfigurationFileMap {ExeConfigFilename = filename};
var config = ConfigurationManager.OpenMappedExeConfiguration(mapping, ConfigurationUserLevel.None);
var section = (CoreConfigurationSection)config.GetSection(_root);
if (section == null)
{
Console.Write("Core: Building core.config...");
section = new CoreConfigurationSection();
config.Sections.Add(_root, section);
Defaults(section);
config.Save(ConfigurationSaveMode.Modified);
Console.WriteLine("done");
}
return config;
}
private static void Defaults(CoreConfigurationSection section)
{
section.Settings["Production"] = "false";
section.Settings["Debug"] = "false";
section.Settings["EventBot"] = "true";
section.Settings["WebAccounting"] = "true";
section.Settings["AllowPlayers"] = "true";
}
#region Accessors
public static string Get(string setting)
{
var config = Load();
var section = (CoreConfigurationSection)config.GetSection(_root);
return section.Settings[setting];
}
public static bool GetBoolean(string setting)
{
var config = Load();
var section = (CoreConfigurationSection)config.GetSection(_root);
return section.Settings[setting].ToLower() == "true";
}
public static void Set(string setting,string value)
{
var config = Load();
var section = (CoreConfigurationSection)config.GetSection(_root);
if (value == null)
section.Settings.Remove(setting);
section.Settings[setting] = value;
config.Save(ConfigurationSaveMode.Modified);
}
public static void SetBoolean(string setting, bool value)
{
var config = Load();
var section = (CoreConfigurationSection)config.GetSection(_root);
section.Settings[setting] = value.ToString();
config.Save(ConfigurationSaveMode.Modified);
}
#endregion
#region Named settings
public static bool Production
{
get { return GetBoolean("Production"); }
set { SetBoolean("Production", value); }
}
public static bool Debug
{
get { return GetBoolean("Debug"); }
set { SetBoolean("Debug", value); }
}
public static bool EventBot
{
get { return GetBoolean("EventBot"); }
set { SetBoolean("EventBot", value); }
}
public static bool WebAccounting
{
get { return GetBoolean("WebAccounting"); }
set { SetBoolean("WebAccounting", value); }
}
public static bool AllowPlayers
{
get { return GetBoolean("AllowPlayers"); }
set { SetBoolean("AllowPlayers", value); }
}
#endregion
}
I couldn't really think of a better way to make typed configurations than hardcoding them, but other than that it seems pretty solid to me, you can create and update configurations at runtime, they are global, editable, and located on my application root, so basically that covers all features I wanted.
The core.config file is created at runtime if it doesn't exist, this is verified whenever you try to load or save a setting, with some default values just to "get started"... (you can skip that "initialization" though.
The core.config file looks like this
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<configSections>
<section name="core" type="Server.CoreConfigurationSection, ServerCore, Version=2.1.4146.38077, Culture=neutral, PublicKeyToken=null" />
</configSections>
<core>
<settings>
<setting name="Production" value="false" />
<setting name="Debug" value="false" />
<setting name="EventBot" value="true" />
<setting name="WebAccounting" value="true" />
<setting name="AllowPlayers" value="true" />
</settings>
</core>
</configuration>
Example from MSDN on making a custom config section that should work as follows,
class RemoteServiceSection : ConfigurationSection
{
[ConfigurationProperty("remoteServices", IsDefaultCollection=false)]
[ConfigurationCollection(typeof(RemoteServiceCollection), AddItemName="addService", ClearItemsName="clearServices",
RemoveItemName="removeService")]
public RemoteServiceCollection Services
{
get
{
return this["remoteServices"] as RemoteServiceCollection;
}
}
}
class RemoteServiceCollection : ConfigurationElementCollection, IList<RemoteServiceElement>
{
public RemoteServiceCollection()
{
RemoteServiceElement element = (RemoteServiceElement)CreateNewElement();
Add(element);
}
public override ConfigurationElementCollectionType CollectionType
{
get
{
return ConfigurationElementCollectionType.AddRemoveClearMap;
}
}
protected override ConfigurationElement CreateNewElement()
{
return new RemoteServiceElement();
}
protected override object GetElementKey(ConfigurationElement element)
{
return ((RemoteServiceElement)element).Hostname;
}
protected override string ElementName
{
get
{
return "remoteService";
}
}
public new IEnumerator<RemoteServiceElement> GetEnumerator()
{
foreach (RemoteServiceElement element in this)
{
yield return element;
}
}
public void Add(RemoteServiceElement element)
{
BaseAdd(element, true);
}
public void Clear()
{
BaseClear();
}
public bool Contains(RemoteServiceElement element)
{
return !(BaseIndexOf(element) < 0);
}
public void CopyTo(RemoteServiceElement[] array, int index)
{
base.CopyTo(array, index);
}
public bool Remove(RemoteServiceElement element)
{
BaseRemove(GetElementKey(element));
return true;
}
bool ICollection<RemoteServiceElement>.IsReadOnly
{
get { return IsReadOnly(); }
}
public int IndexOf(RemoteServiceElement element)
{
return BaseIndexOf(element);
}
public void Insert(int index, RemoteServiceElement element)
{
BaseAdd(index, element);
}
public void RemoveAt(int index)
{
BaseRemoveAt(index);
}
public RemoteServiceElement this[int index]
{
get
{
return (RemoteServiceElement)BaseGet(index);
}
set
{
if (BaseGet(index) != null)
{
BaseRemoveAt(index);
}
BaseAdd(index, value);
}
}
}
class RemoteServiceElement : ConfigurationElement
{
public RemoteServiceElement() { }
public RemoteServiceElement(string ip, string port)
{
this.IpAddress = ip;
this.Port = port;
}
[ConfigurationProperty("hostname", IsKey = true, IsRequired = true)]
public string Hostname
{
get
{
return (string)this["hostname"];
}
set
{
this["hostname"] = value;
}
}
[ConfigurationProperty("ipAddress", IsRequired = true)]
public string IpAddress
{
get
{
return (string)this["ipAddress"];
}
set
{
this["ipAddress"] = value;
}
}
[ConfigurationProperty("port", IsRequired = true)]
public string Port
{
get
{
return (string)this["port"];
}
set
{
this["port"] = value;
}
}
}
}
I am getting the error that says 'Unrecognized element 'addService'. I think I've followed the MSDN article exactly. It can be found here - http://msdn.microsoft.com/en-us/library/system.configuration.configurationcollectionattribute.aspx
Thanks in advance for your help. This is what I wrote in app.config (with brackets of course that dont show up here?):
<remoteServices>
<addService hostname="xxxxxxx" ipAddress="xxx.x.xxx.xx" port="xxxx" >
</remoteServices>
Here is app.config as requested, x'ing out the specific names just for privacy purposes, they are just strings:
<configuration>
<configSections>
<section name="remoteServices" type="AqEntityTests.RemoteServiceSection,
AqEntityTests" allowLocation="true" allowDefinition="Everywhere"/>
</configSections>
<remoteServices>
<addService hostname="xxxxxx.xxxxxxx.com"
ipAddress="xxx.x.xxx.xx"
port="xx" />
</remoteServices>
For future generations:
Your config should look like this:
<configuration>
<configSections>
<section name="remoteServices" type="AqEntityTests.RemoteServiceSection,
AqEntityTests" allowLocation="true" allowDefinition="Everywhere"/>
</configSections>
<remoteServices>
<remoteServices>
<addService hostname="xxxxxx.xxxxxxx.com"
ipAddress="xxx.x.xxx.xx"
port="xx" />
</remoteServices>
</remoteServices>
</configuration>
Why?
You add to node:
<configSections>
custom section named:
name="remoteServices"
with type
type="AqEntityTests.RemoteServiceSection
and then in code, you add property to your custom section:
[ConfigurationProperty("remoteServices", IsDefaultCollection=false)]
Meaning you created node inside node with both having same name. Because of that you have received error "Unrecognized element 'addService'". Just compiler informing you that such element should not be in that node.
Two links for quick learning of custom configuration:
Custom Configuration Sections for Lazy Coders
How to create sections with collections
You might also look into using an unnamed default collection as I mention here
That allows you to add items in the manner you suggest.