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.