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.
-
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)
-
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.
-
What is the "sectionGroup" tag in "configSections" if the "Customization Handler" works, and that's enough if one "section" is registered?
-
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; }
}
}
source to share
Since there is no activity here, I will try to answer my questions one by one:
-
Found this nice article that describes a lot of things and shows a somewhat complex example of custom config implementation.
-
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.
source to share