App.config: custom nested configuration sections

I found a great example for a custom config handler and tried to use it for my own implementation.

I installed App.config like this:

<configSections>
  <section name="DocumentationSettings" type="ConfigHandler.DocumentationSettings,Settings"/>
</configSections>

<DocumentationSettings>
  <DocumentationSections>
    <DocumentationSection Id="AAA">
      <SectionDescription Value="SectionDescriptionAAA"/>
    </DocumentationSection>
    <DocumentationSection Id="BBB">
      <SectionDescription Value="SectionDescriptionBBB"/>
    </DocumentationSection>
    <DocumentationSection Id="CCC">
      <SectionDescription Value="SectionDescriptionCCC"/>
    </DocumentationSection>
  </DocumentationSections>
</DocumentationSettings>

      

I am using this code to access my custom config:

DocumentationSettings documentationSettings = ConfigurationManager.GetSection("DocumentationSettings") as DocumentationSettings;

foreach (DocumentationSectionConfigElement section in (documentationSettings.DocumentationSections.Sections))
{
    Console.WriteLine(section.Id);
    Console.WriteLine(section.SectionDescription.Properties.Value);
}

      

The first "Console.WriteLine" works great.

So, I have the following problems and implementation related questions:

  • I am getting an error in the second "Console.WriteLine", error: unrecognized attribute "Value". I put "public SectionDescription SectionDescription" because the "DocumentationSectionConfigElement" class exposes properties, but I might be wrong, I first tried to put it in "DocumentationSectionCollection" but I don't know how to implement it there and it seems to me that " DocumentationSectionCollection "implements the" Collection "logic.

  • I want to access the "fields" either like this:

    section.Id section.SectionDescription.Value

or like this:

section.Properties.Id
section.SectionDescription.Properties.Value

      

I see that "Collection" allows these properties to be used directly using indexer methods like this:

public DocumentationSectionConfigElement this[int index]

      

But I cannot implement the indexer method in the "SectionDescription" class, because it is a separate section, not a collection, so this "Properties" name is retained when I access the fields.

  1. What do I need to add to be able to use LINQ for these config objects? I mean like this:

    (documentationSettings.DocumentationSections.Sections). Select (x => x.Id)

  2. Are there any really good examples of a complex XML structure configuration constructor ? The ones I found were mostly simple structures like these:

       

but no example of such a complex structure exists:

<section>
  <subSections>
    <subSection name="111">
      <Description Value="AAA"></Description>
      <Headers>
        <Header type="Main">
         <Properties>
           <Property name="Property1"/>
           <Property name="Property2"/>
           <Property name="Property3"/>
         </Properties>
        </Header>
      </Headers>
    </subSection>
  </subSections>
</section>

      

which is closer to real-world scenarios when you need to better organize your application configuration.

  1. What is the "sectionGroup" tag in "configSections" if the "Customization Handler" works, and that's enough if one "section" is registered?

  2. Why is all this so difficult? I've spent so much time on these "configurable" things that shouldn't be that hard I believe. Are there any Visual Studio add-ins that handle this stuff and generate code based on the XML configuration structure?

Here is my implementation of the config handler:

public class DocumentationSettings : ConfigurationSection
{
    [ConfigurationProperty("DocumentationSections")]
    public DocumentationSections DocumentationSections
    {
        get { return (DocumentationSections)base["DocumentationSections"]; }
    }
}

public class DocumentationSections : ConfigurationElement
{
    [ConfigurationProperty("", IsDefaultCollection = true)]
    public DocumentationSectionCollection Sections
    {
        get { return (DocumentationSectionCollection)base[""]; }
    }
}

public class SectionDescription : ConfigurationElement
{
    [ConfigurationProperty("SectionDescription")]
    public new SectionDescriptionConfigElement Properties
    {
        get { return (SectionDescriptionConfigElement)base["SectionDescription"]; }
    }
}

public class DocumentationSectionCollection : ConfigurationElementCollection
{
    public DocumentationSectionCollection()
    {
        DocumentationSectionConfigElement details = (DocumentationSectionConfigElement)CreateNewElement();
        if (details.Id != "")
        {
            Add(details);
        }
    }

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

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

    protected override Object GetElementKey(ConfigurationElement element)
    {
        return ((DocumentationSectionConfigElement)element).Id;
    }

    public DocumentationSectionConfigElement this[int index]
    {
        get { return (DocumentationSectionConfigElement)BaseGet(index); }
        set
        {
            if (BaseGet(index) != null)
            {
                BaseRemoveAt(index);
            }
            BaseAdd(index, value);
        }
    }

    new public DocumentationSectionConfigElement this[string name]
    {
        get { return (DocumentationSectionConfigElement)BaseGet(name); }
    }

    public int IndexOf(DocumentationSectionConfigElement details)
    {
        return BaseIndexOf(details);
    }

    public void Add(DocumentationSectionConfigElement details)
    {
        BaseAdd(details);
    }
    protected override void BaseAdd(ConfigurationElement element)
    {
        BaseAdd(element, false);
    }

    public void Remove(DocumentationSectionConfigElement details)
    {
        if (BaseIndexOf(details) >= 0)
            BaseRemove(details.Id);
    }

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

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

    public void Clear()
    {
        BaseClear();
    }

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

public class DocumentationSectionConfigElement : ConfigurationElement
{
    [ConfigurationProperty("Id", IsRequired = true, IsKey = true)]
    [StringValidator(InvalidCharacters = "  ~!@#$%^&*()[]{}/;’\"|\\")]
    public string Id
    {
        get { return (string)this["Id"]; }
        set { this["Id"] = value; }
    }

    [ConfigurationProperty("Name", IsRequired = false)]
    public string Name
    {
        get { return (string)this["Name"]; }
        set { this["Name"] = value; }
    }

    [ConfigurationProperty("SectionDescription")]
    public SectionDescription SectionDescription
    {
        get { return ((SectionDescription)base["SectionDescription"]); }
    }
}

public class SectionDescriptionConfigElement : ConfigurationElement
{
    [ConfigurationProperty("Value", IsRequired = true)]
    public string Value
    {
        get { return (string)this["Value"]; }
        set { this["Value"] = value; }
    }
}

      

+3


source to share


1 answer


Since there is no activity here, I will try to answer my questions one by one:



  1. Found this nice article that describes a lot of things and shows a somewhat complex example of custom config implementation.

  2. I found this great tool, the .NET Configuration Code Generator , which does exactly what I wanted — it takes an XML structure and generates custom configuration code.

+5


source







All Articles