Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

App.config: custom configuration nested sections

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

I have set up 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 use this code to access my custom configuration:

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

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

First 'Console.WriteLine' works perfect.

So I have the following problems and implementation related questions:

  1. I get error on second 'Console.WriteLine', error: Unrecognized attribute 'Value'. I have put "public SectionDescription SectionDescription" since "DocumentationSectionConfigElement" Class exposes property access, but I might be wrong, I tried first to put it into "DocumentationSectionCollection" but I don't know how to implement it there and for me it seems that "DocumentationSectionCollection" only implements "Collection" logic.

  2. I want to access "fields" either both like this:

    section.Id section.SectionDescription.Value

or like this:

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

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

public DocumentationSectionConfigElement this[int index]

But I can't implement indexer method on "SectionDescription" class, because it is a single section not a collection, so this "Properties" name persists when I access fields.

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

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

  2. Are there any really good examples of complex XML structure configuration handler? From those which I found there were mostly simple structures like these:

but no any example of complex structure like this:

<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 more close to real scenarios when you need to better organize application configuration.

  1. What is the point of "sectionGroup" tag in "configSections" if "Custom Configuration Handler" works and it is enough with one "section" registered ?

  2. Why all this stuff so complex? I have spent so much time these "custom configuration" things that shouldn't be so complex I believe. Aren't there any Visual Studio Add-ins that handle this stuff and generate code based on XML configuration structure?

Here is my Configuration Handler implementation:

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; }
    }
}
like image 767
Vadim K. Avatar asked Apr 26 '15 21:04

Vadim K.


People also ask

When to use custom configsection with nested elements?

It is very useful in cases where you need to store some limited number of configuration details or any data, and you do not want to use database for storing those configurations. Here, I will explain the step by step process to implement custom configSection with nested elements.

Where is the configurationmanager class in the app config file?

One thing to note is that in the app.config file, <configSections> must be the first thing to appear in the <configuration> section, otherwise an error will be thrown at runtime. Also, the ConfigurationManager class is in the System.Configuration namespace, so be sure you have

How do I create a custom section in web config?

In the Web.config file, add a sectionGroup element and a section element inside the configSections element, as shown in the following example. The declaration associates the custom section handler with the section name. Nesting a section element in a sectionGroup is optional, but we recommend doing this to help organize configuration data.

How do I create a nested config section in Salesforce?

Create a new Console application named as “ CustomNestedConfigSection ”. Create a folder under this project named as “ NestedConfig ”. Add a new App.config file to the project. Add a new class file “ EmployeesConfigSection.cs ” in NestedConfig folder as below:


1 Answers

Since no activity here, I will try answering my questions one by one:

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

  2. I found this great tool - ".NET Configuration Code Generator", that does exactly what I wanted - it takes XML structure and generates custom configuration code.

like image 123
Vadim K. Avatar answered Sep 27 '22 22:09

Vadim K.