📜 ⬆️ ⬇️

Read configuration files without problems

In this article I would like to consider the problem of loading settings from configuration files. As a rule, developers use the heavyweight and complex API from the System.Configuration namespace and read the settings step by step. If in the configuration file the section that needs to be considered is a simple structure (without nesting), then, in principle, reading does not cause any special problems. However, as soon as the configuration becomes more complicated and / or nested subsections appear, the dissection turns into a real headache. For easy and fast reading of settings and loading them into memory, the ConfigurationParser library is perfect, which will take over all the difficulties of working with configuration files.

Installation


This library is available for download via NuGet . Or you can download the source from GitHub .

Using


To demonstrate the capabilities of the library, let us imagine that our program requires settings for connecting to several external services, databases, and our system should be able to send letters to system administrators. We decided that these settings will be in three sections.
')
system configuration
<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"/> </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> 


The next step is to create classes in which we will store the system settings.

Configuration classes
  #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 the configuration file we need to declare that we will use ConfigurationParser.

Filling configSections in App.Config
 <?xml version="1.0" encoding="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 read our configuration easily and naturally:

 ExternalSystemSettings externalSystemSettings = (dynamic)ConfigurationManager.GetSection("ExternalSystemSettings"); DatabasesSettings databasesSettings = (dynamic)ConfigurationManager.GetSection("DatabasesSettings"); 

After executing the code, we will create 2 objects, which will contain our settings. As you can fairly note, we do not read SmtpSettings . This is done in order to demonstrate the additional features of the utility.

In various projects, you may need to write various settings to configuration files and have your own logic on how to work with them. The utility is designed in such a way that you can easily expand it by adding a new strategy and covering all the scenarios that you need. For example, let's take a look at the Recipients section:

 <Recipients>clien1@gmail.com;clien2@gmail.com;clien3@gmail.com</Recipients> 

This section contains a list of emails separated by a semicolon. According to our specification and class, we must write each address as a separate element in the array. To accomplish the task, we need to create the SplitRecipientsCustomStrategy class and implement the ICustomMappingStrategy interface .

Customer strategy
 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 } 

Inside, we need to implement the algorithm for parsing the value from the section in accordance with our task. It is necessary to take into account that the Map method (string input, Type itemType) is called if the value is read from the attribute, and the Map method (XmlNode xmlNode, Type itemType) is called if the value is read from the section. According to the configuration file, the value will be read from the section.

After that, we need to mark the Recipients property with the CustomStrategy attribute in which we need to specify what “strategy” will be used for this field:

 [CustomStrategy(typeof(SplitRecipientsCustomStrategy))] public List<string> Recipients { get; set; } 

That's it, now we can read email settings.

 SmtpSettings smtpSettings = (dynamic)ConfigurationManager.GetSection("SmtpSettings"); 

As you can see, ConfigurationParser makes the job of reading settings from the configuration file very convenient and saves a lot of time. At the same time, it is easily expanded due to the connection of new strategies.

I would like to mention one more possibility of this library. This is the PropertyMappingAttribute attribute. It should be used in the case when the name of the element in the configuration file does not match the name of the property.
For example, we have an Email section where the current email address of the user is recorded.
 <Email>clien1@gmail.com</Email> 

But in the class to store this value we have provided the UserEmail property. In order for the settings to be considered correctly, it is necessary to change this property with the attribute PropertyMappingAttribute .
 [PropertyMapping("Email")] public string UserEmail { get; set; } 

Thus, the names of the elements in the configuration file do not have to be the same as the names of the properties.

If you have any questions or suggestions, please write them in the comments, I will be happy to answer them.

Source: https://habr.com/ru/post/324868/


All Articles