0
votes

So, I'm trying to make a custom configuration section for my app.config that will process the following pattern:

  <domainSolutionSection>
    <domainSolutionGroups>
      <Group groupname="Legacy">
        <add name="Old App #1"/>
        <add name="Old App #2" />
      </Group>
      <Group groupname="Modern">
        <add name="New App #1" />
        <add name="New App #2" />
        <add name="New App #3" />
      </Group>
    </domainSolutionGroups>
  </domainSolutionSection>

I've spent all day trying to get this to work.
Right now, I'm getting an error:

System.Configuration.ConfigurationErrorsException: Unrecognized element 'add'.

Here are the classes in my DomainSolutionSection library:

public class DomainSolutionSection : ConfigurationSection
{
    [ConfigurationProperty("domainSolutionGroups")]
    public DomainSolutionGroupCollection DomainSolutionGroups
    {
        get
        {
            return (DomainSolutionGroupCollection) this["domainSolutionGroups"];
        }
        set
        {
            this["domainSolutionGroups"] = value;
        }
    }
}

[ConfigurationCollection(typeof(GroupConfigElement))]
public class DomainSolutionGroupCollection : ConfigurationElementCollection
{
    protected override ConfigurationElement CreateNewElement()
    {
        return new GroupConfigElement();
    }

    protected override object GetElementKey(ConfigurationElement element)
    {
        return ((GroupConfigElement)element).GroupName;
    }

    protected override string ElementName
    {
        get { return "Group"; }
    }

    protected override bool IsElementName(string elementName)
    {
        return !String.IsNullOrEmpty(elementName) && elementName == "Group";
    }

    public override ConfigurationElementCollectionType CollectionType
    {
        get
        {
            return ConfigurationElementCollectionType.BasicMap;
        }
    }

    public GroupConfigElement this[int index]
    {
        get
        {
            return base.BaseGet(index) as GroupConfigElement;
        }
    }

    public new GroupConfigElement this[string key]
    {
        get
        {
            return base.BaseGet(key) as GroupConfigElement;
        }
    }
}
   
public class GroupConfigElement : ConfigurationElement
{
    [ConfigurationProperty("groupname", IsRequired = true, IsKey = true)]
    public string GroupName
    {
        get { return (string)this["groupname"]; }
        set { this["groupname"] = value; }
    }

    public PhraseCollection Phrases
    {
        get { return (PhraseCollection) base["Groups"]; } 
    }
}

[ConfigurationCollection(typeof(PhraseConfigElement), AddItemName = "add", CollectionType = ConfigurationElementCollectionType.BasicMap)]
public class PhraseCollection: ConfigurationElementCollection
{
    protected override ConfigurationElement CreateNewElement()
    {
        return new PhraseConfigElement();
    }

    protected override object GetElementKey(ConfigurationElement element)
    {
        return ((PhraseConfigElement)element).Name;
    }
}

public class PhraseConfigElement: ConfigurationElement
{
    [ConfigurationProperty("name", IsRequired = true, IsKey = true)]
    public string Name
    {
        get { return (string)this["name"]; }
        set { this["name"] = value; }
    }
}

Not finding any examples out there where "add" properties are used at this level of nesting, so obviously, I haven't "wired-up" the "Group" properly. Can anybody offer any guidance?

Thanks!

2

2 Answers

0
votes

Apologies that this is not a direct answer to your question. I don't have the time to break down what you have done, but here is a similar example of something that I have done in the past, and which works. Perhaps there's something in here to help.

myconfig.config:

<?xml version="1.0"?>
<myconfig>
  <moduleIPAccessRules>
    <allowRules>
      <add module="ModuleA"   subnetIP="172.16.0.0"     subnetMask="255.255.0.0"      name="ModuleA intranet users" />
      <add module="ModuleA"   subnetIP="127.0.0.0"      subnetMask="255.255.0.0"      name="ModuleA local box" />

      <add module="ModuleB"     subnetIP="172.16.0.0"     subnetMask="255.255.0.0"      name="ModuleB intranet users" />
      <add module="ModuleB"     subnetIP="127.0.0.0"      subnetMask="255.255.0.0"      name="ModuleB local box" />
      <add module="ModuleB"     subnetIP="123.45.67.89"   subnetMask="255.255.255.255"  name="some remote machine" />
    </allowRules>
  </moduleIPAccessRules>
</myconfig>

Backing class:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Configuration;

namespace Myproject.Configuration
{
    public class IPAccessSection : ConfigurationSection
    {
        public static IPAccessSection GetConfig()
        {
            return ConfigurationManager.GetSection("myconfig/moduleIPAccessRules") as IPAccessSection;
        }

        [ConfigurationProperty("allowRules")]
        public AllowRuleCollection AllowRules
        {
            get { return this["allowRules"] as AllowRuleCollection; }
        }
    }

    public class AllowRuleCollection : ConfigurationElementCollection
    {
        public AllowRule this[int index]
        {
            get
            {
                return base.BaseGet(index) as AllowRule;
            }
            set
            {
                if (base.BaseGet(index) != null)
                    base.BaseRemoveAt(index);

                this.BaseAdd(index, value);
            }
        }

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

        protected override object GetElementKey(ConfigurationElement element)
        {
            return ((AllowRule)element).Name;
        }
    }

    public class AllowRule : ConfigurationElement
    {
        [ConfigurationProperty("name", IsRequired = true)]
        public string Name
        {
            get { return this["name"] as string; }
        }

        [ConfigurationProperty("module", IsRequired = true)]
        public string Module
        {
            get { return this["module"] as string; }
        }

        [ConfigurationProperty("subnetIP", IsRequired = true)]
        public string SubnetIP
        {
            get { return this["subnetIP"] as string; }
        }

        [ConfigurationProperty("subnetMask", IsRequired = true)]
        public string SubnetMask
        {
            get { return this["subnetMask"] as string; }
        }
    }
}
0
votes

Actually, I just figured out the answer about 10 minutes ago. And it's basically that I hadn't really "hooked up" the PhraseCollection to the GroupConfigElement properly... nor had I fully fleshed-out the PhraseCollection.:

    public class GroupConfigElement : ConfigurationElement
{
    [ConfigurationProperty("groupname", IsRequired = true, IsKey = true)]
    public string GroupName
    {
        get { return (string)this["groupname"]; }
        set { this["groupname"] = value; }
    }

    [ConfigurationProperty("", IsDefaultCollection = true, IsRequired = true)]
    public PhraseCollection Phrases
    {
        get { return (PhraseCollection) base[""]; }
    }
}

[ConfigurationCollection(typeof(PhraseConfigElement))]
public class PhraseCollection: ConfigurationElementCollection
{
    protected override ConfigurationElement CreateNewElement()
    {
        return new PhraseConfigElement();
    }

    protected override object GetElementKey(ConfigurationElement element)
    {
        return ((PhraseConfigElement)element).Name;
    }

    public PhraseConfigElement this[int index]
    {
        get
        {
            return base.BaseGet(index) as PhraseConfigElement;
        }
    }

    public new PhraseConfigElement this[string key]
    {
        get
        {
            return base.BaseGet(key) as PhraseConfigElement;
        }
    }
}

public class PhraseConfigElement: ConfigurationElement
{
    [ConfigurationProperty("name", IsRequired = true, IsKey = true)]
    public string Name
    {
        get { return (string)this["name"]; }
        set { this["name"] = value; }
    }
}

}

The key fixes were to the "Phrases" definition... and then I had to add those two public methods to the "PhraseCollection" definition.

Thanks!