Get ConfigurationElement parent in app.config

1.1k views Asked by At

I have created a custom ConfigurationSection, ConfigurationElement(s) and ConfigurationElementCollection(s) for my app.config, it's based on this documentation.

Now I would like to be able to access a parent element in any configuration element.
For example something in the lines of the following:

public class CustomSection : ConfigurationSection
{
    [ConfigurationProperty("child")]
    public ChildElement Child
    {
        get { return (ChildElement)this["child"]; }
        set { this["child"] = value; }
    }
}

public class ChildElement : ConfigurationElement
{
    [ConfigurationProperty("name")]
    public string Name
    {
        get { return (string)this["name"]; }
        set { this["name"] = value; }
    }

    [ConfigurationProperty("nestedchild")]
    public NestedChildElement NestedChild
    {
        get { return (NestedChildElement)this["nestedchild"]; }
        set { this["nestedchild"] = value; }
    }
}

public class NestedChildElement : ConfigurationElement
{
    [ConfigurationProperty("name")]
    public string Name
    {
        get { return (string)this["name"]; }
        set { this["name"] = value; }
    }

    public void Sample()
    {
        // How can I access parent ChildElement object
        // and also its parent CustomSection object from here?
    }
}

Is there something in the base ConfigurationElement class that I'm missing and that would enable me to do this?

I'm hoping if it's possible to achieve this with some kind of generic solution;
One that would not require to introduce something like a Parent property on each element and then need to assign that property value in each ConfigurationProperty getter.

1

There are 1 answers

0
Mario Z On BEST ANSWER

You haven't missed anything in ConfigurationElement, it's unable to give you any hierarchy or order information. You'll have to keep this information yourself, for example see this answer.

For a generic solution you may want to check out my POC for defining parent placeholders in app.config.
As a side note, in previous version I've done this with interface (you could check previous commits) and in the current version with extension property.

Also, the following is a trimmed down version that accomplishes just your requirement:

public abstract class ConfigurationElementBase : ConfigurationElement
{
    protected T GetElement<T>(string name) where T : ConfigurationElement
        => this.GetChild<T>(name);
}

public abstract class ConfigurationSectionBase : ConfigurationSection
{
    protected T GetElement<T>(string name) where T : ConfigurationElement
        => this.GetChild<T>(name);
}

public static class ConfigurationExtensions
{
    private static readonly Dictionary<ConfigurationElement, ConfigurationElement> Parents =
        new Dictionary<ConfigurationElement, ConfigurationElement>();

    public static T GetParent<T>(this ConfigurationElement element) where T : ConfigurationElement
        => (T)Parents[element];

    private static void SetParent(this ConfigurationElement element, ConfigurationElement parent)
        => Parents.Add(element, parent);

    private static object GetValue(this ConfigurationElement element, string name)
        => element.ElementInformation.Properties.Cast<PropertyInformation>().First(p => p.Name == name).Value;

    internal static T GetChild<T>(this ConfigurationElement element, string name) where T : ConfigurationElement
    {
        T childElement = (T)element.GetValue(name);
        if (!Parents.ContainsKey(childElement))
            childElement.SetParent(element);
        return childElement;
    }
}

Now you can use these base configuration classes in your custom section as following:

public class CustomSection : ConfigurationSectionBase
{
    [ConfigurationProperty("name")]
    public string Name
    {
        get { return (string)this["name"]; }
        set { this["name"] = value; }
    }

    [ConfigurationProperty("child")]
    public ChildElement Child => base.GetElement<ChildElement>("child");
}

public class ChildElement : ConfigurationElementBase
{
    [ConfigurationProperty("name")]
    public string Name
    {
        get { return (string)this["name"]; }
        set { this["name"] = value; }
    }

    [ConfigurationProperty("nestedchild")]
    public NestedChildElement NestedChild => base.GetElement<NestedChildElement>("nestedchild");
}

public class NestedChildElement : ConfigurationElement
{
    [ConfigurationProperty("name")]
    public string Name
    {
        get { return (string)this["name"]; }
        set { this["name"] = value; }
    }

    public void Sample()
    {
        ChildElement parentChildElement = this.GetParent<ChildElement>();
        CustomSection parentCustomSection = parentChildElement.GetParent<CustomSection>();
        // TODO Use the parents ...
    }