There are many ways to read configuraiton files in .Net. Using ConfigurationManager you can easily read for example appSettings section. But usually I want this to be in managed code. The solution often suggested is to create separate sections with a varying degree of manual work, field mapping and xml readers.
Many of the solutions also suggest that you read and convert the type from string on every access, which gives you an overhead if you access config variables a lot. So I figured I’d throw together something simple that works. A baseclass that supports a tree structure without all the hassle of creating sections, defining types and names, etc… It reads and converts the variables once at startup to avoid conversion overhead on every access of properties.
In short, Config.SmtpClient.Host  corresponds to appSettings value “SmtpClient.Hosts“. You can add as many levels as you like.
app.config with appSettings section
The config variable names corresponds to properties in the classes. When there is a hierarchy of classes the names in the path will be separated by a dot.
1 2 3 4 5 6 |
<configuration> <appSettings> <add key="SmtpClient.Host" value="localhost" /> <add key="SpecialWebApi.Url" value="https://some.api.org/" /> </appSettings> </configuration> |
Creating the config object
Config = new ConfigModel();
The implementation
In the implementation of config model we can create a hierarchy of models. As long as they inherid DictionaryConfigReader  and override the mandetory base class constructor they will automatically read config variables corresponding to their name.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
public class ConfigModel : DictionaryConfigReader { public ConfigModel(Dictionary<string, string> config = null, string prefix = "") : base(config, prefix) { } public SmtpClientModel SmtpClient { get; private set; } public SpecialWebApiModel SpecialWebApi { get; private set; } } public class SmtpClientModel : DictionaryConfigReader { public SmtpClientModel(Dictionary<string, string> config = null, string prefix = "") : base(config, prefix) { } public string Host { get; private set; } } public class SpecialWebApiModel : DictionaryConfigReader { public SpecialWebApiModel(Dictionary<string, string> config = null, string prefix = "") : base(config, prefix) { } public string Url { get; private set; } } |
Baseclass
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 |
public abstract class DictionaryConfigReader { public static Dictionary<string, string> GetConfig() { var ret = new Dictionary<string, string>(); foreach (var key in ConfigurationManager.AppSettings.AllKeys) ret.Add(key, ConfigurationManager.AppSettings[key]); return ret; } public DictionaryConfigReader(Dictionary<string, string> config = null, string prefix = "") { if (config == null) config = DictionaryConfigReader.GetConfig(); if (prefix == null) prefix = ""; if (!string.IsNullOrEmpty(prefix)) prefix += "."; var type = this.GetType(); string value; foreach (var property in type.GetProperties()) { // Child objects are populated similarly if (property.PropertyType.BaseType == typeof(DictionaryConfigReader)) { var prefix2 = prefix + property.Name; var obj = Activator.CreateInstance(property.PropertyType, config, prefix2); property.SetValue(this, obj, null); continue; } if (config.TryGetValue(prefix + property.Name, out value)) { // Here we are converting a string into a Type. If we have any special types then Convert.ChangeType or TypeConverter probably will fail. // In these cases, use something like this: //if (property.PropertyType == typeof(AwesomeClass)) //{ // property.SetValue(this, new AwesomeClass(value), null); // continue; //} // Some takes on dynamic type conversion // Version 1 //var typeConverter = TypeDescriptor.GetConverter(property.PropertyType); //object propValue = typeConverter.ConvertFromString(value); //property.SetValue(this, propValue); // Version 2 //property.SetValue(this, Convert.ChangeType(value, property.PropertyType), null); // Version 3: With support for nullable types var t = Nullable.GetUnderlyingType(property.PropertyType) ?? property.PropertyType; var safeValue = (value == null) ? null : Convert.ChangeType(value, t); property.SetValue(this, safeValue, null); } } } } |