Introduction
In this article I would like to focus on problems people face with when they get settings from config files. Usually developers use comprehensive and complicated API from System.Configuration namespace and get settings step by step. In case when section we need to read has a simple structure (without nesting), then reading it doesn’t cause much troubles. However, when structure is more complex and/or has nested subsections, then parsing it might become a real headache. ConfigurationParser utility is specifically designed to overcome such problems and make read from config files a quick and simple process.
Installation
The library is available in NuGet. Alternatively you can get the source code from GitHub.
Usage
In order to demonstrate the power of the library, let’s imagine that our program requires settings for connecting to several external systems and databases as well as being able to send emails to system administrators. We decide that these settings should be placed into three sections.
<ExternalSystemSettings>
<Authenticationsettings>
<login>DotNetCraft</login>
<token>qwerty</token>
<Urls>
<url>https://github.com/DotNetCraft/ConfigurationParser</url>
<url>https://github.com/DotNetCraft/ConfigurationParser</url>
</Urls>
</Authenticationsettings>
<Staffsettings token="{D0C148F7-83C0-41B0-8F18-B47CAB09AD99}" url="https://github.com/DotNetCraft/ConfigurationParser"/>
</ExternalSystemSettings>
<DatabasesSettings>
<Mongosettings connectionstring="mongo.url" databasename="DotNetCraft"/>
<Sqlsettings>
<item key="TenantA">
<value>
<sqlsettings connectionstring="sql.TenantA.com">
</sqlsettings></value>
</item>
<item>
<key>TenantB</key>
<value>
<sqlsettings>
<connectionstring>sql.TenantB.com</connectionstring>
</sqlsettings>
</value>
</item>
</Sqlsettings>
</DatabasesSettings>
<SmtpSettings host="gmail.com" sender="no-reply">
<Recipients>clien1@gmail.com;clien2@gmail.com;clien3@gmail.com</Recipients>
</SmtpSettings>
Our next step is to create classes which will store the system settings.
#region ExternalSystemSettings
class ExternalSystemSettings
{
public AuthenticationServiceSettings AuthenticationSettings { get; set; }
public StaffServiceSettings StaffSettings { get; set; }
}
class AuthenticationServiceSettings
{
public string Login { get; set; }
public string Token { get; set; }
public List<string> Urls { get; set; }
}
class StaffServiceSettings
{
public Guid Token { get; set; }
public string Url { get; set; }
}
#endregion
#region DatabasesSettings
class DatabasesSettings
{
public MongoDatabaseSettings MongoSettings { get; set; }
public Dictionary<string, SqlSettings> SqlSettings { get; set; }
}
class MongoDatabaseSettings
{
public string ConnectionString { get; set; }
public string DatabaseName { get; set; }
}
class SqlSettings
{
public string ConnectionString { get; set; }
}
#endregion
#region Smtp
class SmtpSettings
{
public string Host { get; set; }
public string Sender { get; set; }
[CustomStrategy(typeof(SplitRecipientsCustomStrategy))]
public List<string> Recipients { get; set; }
}
#endregion
In config file we need to declare that we will be using ConfigurationParser.
="1.0"="utf-8"
<configuration>
<configSections>
<section name="ExternalSystemSettings" type="DotNetCraft.ConfigurationParser.SimpleConfigurationSectionHandler, DotNetCraft.ConfigurationParser" />
<section name="DatabasesSettings" type="DotNetCraft.ConfigurationParser.SimpleConfigurationSectionHandler, DotNetCraft.ConfigurationParser" />
<section name="SmtpSettings" type="DotNetCraft.ConfigurationParser.SimpleConfigurationSectionHandler, DotNetCraft.ConfigurationParser" />
</configSections>
…
</configuration>
Now we can easily read our configuration the following way:
ExternalSystemSettings externalSystemSettings = (ExternalSystemSettings)(dynamic)ConfigurationManager.GetSection("ExternalSystemSettings");
or
ExternalSystemSettings externalSystemSettings = (dynamic)ConfigurationManager.GetSection("ExternalSystemSettings");
After this code is executed we will have two object containing our settings. You might notice that we are not currently getting SmtpSettings. This is done in order to show additional abilities of the utility.
In different projects you might need to have various settings in configuration files and have your own logic how you process them. The utility is designed in a way that allows you to easily extend it by adding new strategies and in doing that cover all scenarios that you require. As an example, let’s have a look at Recipients section:
<Recipients>clien1@gmail.com;clien2@gmail.com;clien3@gmail.com</Recipients>
This section contains a list of email addresses separated by semicolon. According to our requirements we and models we need to write each email as a separate element to an array. In order to achieve that we’re creating SplitRecipientsCustomStrategy
class and implement ICustomMappingStrategy
interface.
class SplitRecipientsCustomStrategy : ICustomMappingStrategy
{
#region Implementation of ICustomMappingStrategy
public object Map(string input, Type itemType)
{
string[] items = input.Split(';');
List<string> result = new List<string>();
result.AddRange(items);
return result;
}
public object Map(XmlNode xmlNode, Type itemType)
{
string input = xmlNode.InnerText;
return Map(input, itemType);
}
#endregion
}
In this class we implement an algorithm we require to parse value from the section according to the task. Note that method Map(string input, Type itemType)
is called if value was read from attribute while Map(XmlNode xmlNode, Type itemType)
is called if value was read from section. In our case the value will be read from section.
After that we need to mark property Recipients
with CustomStrategy
attribute in which we write what strategy we would like to use for this field:
[CustomStrategy(typeof(SplitRecipientsCustomStrategy))]
public List<string> Recipients { get; set; }
That’s it, now we’re able to read email setting.
SmtpSettings smtpSettings = (dynamic)ConfigurationManager.GetSection("SmtpSettings");
As you can see, ConfigurationParser makes the process of reading config setting simple and smooth and saves you a lot of time. Moreover, the utility is easily extensible thanks to ability to introduce new strategies.
Conclusion
As you can see, ConfigurationParser makes the process of reading config setting simple and smooth and saves you a lot of time. Moreover, the utility is easily extensible thanks to ability to introduce new strategies.
I you have any questions or suggestions, please feel free to write them in comments and I will be glad to answer them.