Code Review Stack Exchange is a question and answer site for peer programmer code reviews. It's 100% free, no registration required.

Sign up
Here's how it works:
  1. Anybody can ask a question
  2. Anybody can answer
  3. The best answers are voted up and rise to the top

I wanted to have a custom section in my app.config file in which any element could reference and reuse (or better said could inherit) any string value that is defined in it or above it in the element hierarchy. In other words, a child element could reuse any string value that it has or that its parents have.

For example, consider the following app.config file's section:

<fooSection root="C:\Root">
  <fooCollection folder="FooFolder">
    <fooElement name="Bar1" path="$(root)\$(folder)\$(name)" />
    <fooElement name="Bar2" path="$(root)\$(folder)\$(name)" />
  </fooCollection>
</fooSection>

When reading this section:

var fooSection = (FooSection)ConfigurationManager.GetSection("fooSection");
foreach (var fooElement in fooSection.FooCollection)
    Console.WriteLine(fooElement.Path);

I wanted to achieve the following result:

C:\Root\FooFolder\Bar1
C:\Root\FooFolder\Bar2

Currently I've done this with the following:

public interface IInheritedConfiguration
{
    IInheritedConfiguration Parent { get; set; }
    bool TryGetValueCore(string name, out string value);
    T GetElementCore<T>(string name) where T : IInheritedConfiguration;
}

public static class InheritedConfigurationExtension
{
    private static readonly Regex inheritPlaceholderRegex =
        new Regex(@"\$\([\w.]+\)", RegexOptions.Compiled | RegexOptions.CultureInvariant);

    public static string GetInheritedValue(this IInheritedConfiguration element, string name)
    {
        string value;
        if (!element.TryGetValueCore(name, out value))
            return null;

        foreach (Match match in inheritPlaceholderRegex.Matches(value))
        {
            IInheritedConfiguration parentElement = element;
            string placeholder = match.Value;
            string placeholderName = placeholder.Substring(2, placeholder.Length - 3);
            string placeholderValue = null;

            do
            {
                placeholderValue = parentElement.GetInheritedValue(placeholderName);
                parentElement = parentElement.Parent;
            }
            while (string.IsNullOrEmpty(placeholderValue) && parentElement != null);

            value = value.Replace(placeholder, placeholderValue);
        }

        return value;
    }

    public static T GetInheritedElement<T>(this IInheritedConfiguration element, string name) where T : IInheritedConfiguration
    {
        T childElement = element.GetElementCore<T>(name);

        if (childElement.Parent == null)
            childElement.Parent = element;

        return childElement;
    }
}
public abstract class ConfigurationSectionBase : ConfigurationSection, IInheritedConfiguration
{
    public IInheritedConfiguration Parent { get; set; }

    protected string GetValue(string name) { return this.GetInheritedValue(name); }

    protected T GetElement<T>(string name) where T : IInheritedConfiguration { return this.GetInheritedElement<T>(name); }

    bool IInheritedConfiguration.TryGetValueCore(string name, out string value)
    {
        value = null;
        if (!base.Properties.Contains(name))
            return false;

        value = base[name].ToString();
        return true;
    }

    T IInheritedConfiguration.GetElementCore<T>(string name) { return (T)base[name]; }
}

public abstract class ConfigurationElementBase : ConfigurationElement, IInheritedConfiguration
{
    public virtual string Key { get { return null; } }

    public IInheritedConfiguration Parent { get; set; }

    protected string GetValue(string name) { return this.GetInheritedValue(name); }

    protected T GetElement<T>(string name) where T : IInheritedConfiguration { return this.GetInheritedElement<T>(name); }

    bool IInheritedConfiguration.TryGetValueCore(string name, out string value)
    {
        value = null;
        if (!base.Properties.Contains(name))
            return false;

        value = base[name].ToString();
        return true;
    }

    T IInheritedConfiguration.GetElementCore<T>(string name) { return (T)base[name]; }
}

public abstract class ConfigurationElementBaseCollection<U> : ConfigurationElementCollection, IInheritedConfiguration
    where U : ConfigurationElementBase, new()
{
    public IInheritedConfiguration Parent { get; set; }

    protected override ConfigurationElement CreateNewElement() { return new U(); }
    protected override object GetElementKey(ConfigurationElement element) { return ((U)element).Key; }

    protected string GetValue(string name) { return this.GetInheritedValue(name); }

    public U this[int index] { get { return this.GetElement(index); } }

    public new IEnumerator<U> GetEnumerator()
    {
        int count = base.Count;
        for (int i = 0; i < count; i++)
            yield return this.GetElement(i);
    }

    private U GetElement(int index)
    {
        var element = (U)base.BaseGet(index);

        if (element.Parent == null)
            element.Parent = this;

        return element;
    }

    bool IInheritedConfiguration.TryGetValueCore(string name, out string value)
    {
        value = null;
        if (!base.Properties.Contains(name))
            return false;

        value = base[name].ToString();
        return true;
   }

   T IInheritedConfiguration.GetElementCore<T>(string name) { return (T)base[name]; }
}

But I'm not pleased with this solution, for starters I have the same TryGetValueCore and GetElementCore implementations in ConfigurationSectionBase, ConfigurationElementBase and ConfigurationElementBaseCollection. I'm hoping someone can advise me on how to achieve this better, actually any suggestions for improvements are appreciated.

share|improve this question
up vote 0 down vote accepted

I've done some modifications on the above implementation.
For example, I replaced TryGetValueCore and GetElementCore with the ContainsValueCore and GetValueCore. The following is their implementation:

bool IInheritedConfiguration.ContainsValueCore(string name)
{
    return base.Properties.Contains(name);
}

object IInheritedConfiguration.GetValueCore(string name)
{
    return base[name];
}

Also I added a support for defining Numeric and Date and Time Format inside a placeholder, for instance $(placeholderName \@ MMMM dd, yyyy).

The modified code is available on my GitHub.

share|improve this answer

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.