Click here to Skip to main content
Click here to Skip to main content

Extraction of configuration from custom configuration file

, 14 May 2013
Rate this:
Please Sign up or sign in to vote.
Library for extracting configuration settings from custom configuration file is provided.

Introduction

App.config is very useful in most of .NET applications. Although this file is enough for many applications, there are cases when it's reasonable to divide your configuration into several files. There also can be a situation when it's more natural to present configuration as plain text rather than xml file. Current library is created for easy extraction of configuration in this case and allows you to change extraction behavior so that you can extract configuration from custom configuration files of different structure.

Background

The idea of the library is rather simple: client has some class whose properties (which should be extracted from configuration file) are marked by special attribute. Configuration extractor then creates the instance of the class and extracts values for these properties. This idea has much in common with that of ConfigurationPropertyAttribute. You can find more info on usage of ConfigurationPropertyAttribute at MSDN and Code Project.

Code structure

There are 4 essential parts of the ConfigurationExtractor library:

  • ConfigurationValueAttribute. Allows you to mark properties you want to be extracted from configuration file. You can set alias for the property and type of converter to convert from string representation.
  • ConfigurationExtractor. Generic type that allows you to create an instance of your configuration class. Uses IValueExtractor specified by client. Configuration class is created via Activator.CreateInstance() so it must have default constructor.
  • IValueExtractor. Allows you to create key-value pairs from the given text where key is property name or alias and value is it's value in string representation.
  • IStringConverter. Provides a conversion from string representation to actual value. Concrete implementations of this interface must have a default constructor.

Library contains several implementations of IValueExtractor for value extraction from plain text (PlainTextExtractor), plain text divided into several sections (PlainTextSectionExtractor) and xml structured text (XmlExtractor). It also contains some implementations of IStringConverter for the most typical conversion to Int32, Int64, Double, Decimal, DateTime etc. Client can create his own implementations (some examples are shown in Example console application shipped with the source code and in Using the code section).

Here are some examples of what is meant by different types of text in configuration file.

Plain text:

a=123
b=456

Plain text with sections:

[sectionA]
a=123

[sectionB]
b=456

XML

<config>
<a>123</a>
<b>456</b>
</config>

Using the code

Here are some examples of using the code. The same examples can be found in Example application in source code.

First, simple configuration is considered. Simple here means that configuration consists of some values of standard types. For example:

class Config
{
    [ConfigurationValue(LongName = "BoolVal", ShortName = "BVal", 
      ConverterType = typeof(StringBoolConverter), DefaultValue = "False")]
    public bool BoolValue { get; set; }

    [ConfigurationValue(LongName = "DoubleVal", ShortName = "DVal", 
      ConverterType = typeof(StringDoubleConverter), DefaultValue = "36.6")]
    public double DoubleValue { get; set; }
}

The instance of this class can be achieved the following way (given the plain text configuration file):

ConfigurationExtractor<Config> converter = 
  new ConfigurationExtractor<Config>(new PlainTextExtractor());

string confFile = System.IO.Path.Combine(System.IO.Directory.GetCurrentDirectory(), 
                                         "PlainTextConfig.cfg");
var conf = converter.Convert(System.IO.File.ReadAllText(confFile));

Plain text configuration file for this case can contain the following text:

BoolVal=True
DVal=12.2

If some more complex configuration is needed than client has to modify value extraction by creating new implementation of IStringConverter. For example, you can easily create list of elements for your configuration. Consider the following configuration class:

class ExtendedConfig
{
    [ConfigurationValue(LongName = "MonthsList", ShortName = "M", 
      ConverterType = typeof(StringMonthsConverter), DefaultValue = "Jan,Sep")]
    public List<Months> Months { get; set; }
}

enum Months
{
    Jan, Feb, Mar, Apr, May, Jun, Jul, Aug, Sep, Oct, Nov, Dec
}

You can extract list of values using following configuration:

M=Jan,Feb,Oct,Sep

and following converter:

public class StringMonthsConverter: IStringConverter
{
    static Dictionary<string, Months> _corresp = new Dictionary<string, Months>
    {
        {"Jan", Months.Jan},
        {"Feb", Months.Feb},
        {"Mar", Months.Mar},
        {"Apr", Months.Apr},
        {"May", Months.May},
        {"Jun", Months.Jun},
        {"Jul", Months.Jul},
        {"Aug", Months.Aug},
        {"Sep", Months.Sep},
        {"Oct", Months.Oct},
        {"Nov", Months.Nov},
        {"Dec", Months.Dec},
    };

    public object Convert(string obj)
    {
        string[] strings = obj.Split(',');

        return strings.Select(x => _corresp[x]).ToList();
    }
}

Extraction itself remains the same:

ConfigurationExtractor<ExtendedConfig> converter = 
  new ConfigurationExtractor<ExtendedConfig>(new PlainTextExtractor());

string confFile = System.IO.Path.Combine(
  System.IO.Directory.GetCurrentDirectory(), "ExtendedConfig.cfg");
var conf = converter.Convert(System.IO.File.ReadAllText(confFile));

The most complex case is when you have some complex types in configuration class. In this case values inside complex type can be also regarded as configuration values. For example:

class ComplexConfig_Text
{
    [ConfigurationValue(LongName = "connection", ShortName = "con", 
      ConverterType = typeof(ConnectionConfigConverter), 
      DefaultValue = PlainTextExtractor.DefaultValue)]
    public ConnectionConfig ConnectionConfig { get; set; }
    [ConfigurationValue(LongName = "user", ShortName = "user", 
      ConverterType = typeof(UserConfigConverter), 
      DefaultValue = PlainTextExtractor.DefaultValue)]
    public UserConfig UserConfig { get; set; }
}

class ConnectionConfig
{
    [ConfigurationValue(LongName = "Address", 
      ShortName = "Adr", DefaultValue = "localhost")]
    public string RemoteAddress { get; set; }
}

class UserConfig
{
    [ConfigurationValue(LongName = "UserID", ShortName = "user", 
      ConverterType = typeof(StringStringSpecialConverter), 
      DefaultValue = StringStringSpecialConverter.Empty)]
    public string UserID { get; set; }
    [ConfigurationValue(LongName = "Password", ShortName = "pwd", 
      ConverterType = typeof(StringStringSpecialConverter), 
      DefaultValue = StringStringSpecialConverter.Empty)]
    public string Password { get; set; }
}

(Note that PlainTextExtractor.DefaultValue is used for default value of ConnectionConfig and UserConfig. This default value is simply an empty string, so when default value is applied this configuration classes are created as if there were no configuration for them. This allows RemoteAddress, UserID, Password to get their initial values as specified in ConfigurationValueAttribute.)

Configuration file for this case can be plain text with sections:

[connection]
Address=google.com

[user]
UserID=Admin
Password=12345

One needs to use PlainTextSectionExtractor to extract value for ComplexConfig_Text and to create special converters for extraction of ConnectionConfig and UserConfig. Since ConnectionConfig and UserConfig are configuration classes themselves, these converters should use ConfigurationExtractor with PlainTextExtractor to extract their values. Here how it looks like:

class ConnectionConfigConverter : IStringConverter
{
    public object Convert(string obj)
    {
        return new ConfigurationExtractor<ConnectionConfig>(
                       new PlainTextExtractor()).Convert(obj);
    }
}

class UserConfigConverter : IStringConverter
{
    public object Convert(string obj)
    {
        return new ConfigurationExtractor<UserConfig>(
                      new PlainTextExtractor()).Convert(obj);
    }
}

The extraction of ComplexConfig_Text looks the following way:

ConfigurationExtractor<Example.Text.ComplexConfig_Text> converter = 
  new ConfigurationExtractor<Example.Text.ComplexConfig_Text>(
  new PlainTextSectionExtractor());

string confFile = System.IO.Path.Combine(
  System.IO.Directory.GetCurrentDirectory(), "ComplexTextConfig.cfg");
var conf = converter.Convert(System.IO.File.ReadAllText(confFile));

Discussion

Solution provided in this article is not very general but I like it for it's simplicity. It's easy to extend it's functionality to gain desirable data in the form you want it. In addition it provides you with unified way to deal with external configuration files (as opposed to app.config which is considered as internal). Some functionality is worth being added to this library: it provides only "get" functionality and it would be nice to have some "set" one, i.e. the possibility to rewrite properties in the configuration file.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

About the Author

Serdar Kurbanov
Software Developer (Junior)
Russian Federation Russian Federation
No Biography provided

Comments and Discussions

 
GeneralMy vote of 5 Pinmemberfredatcodeproject15-May-13 1:03 
GeneralRe: My vote of 5 PinmemberSerdar Kurbanov15-May-13 19:57 
GeneralMy vote of 4 Pinmemberfredatcodeproject14-May-13 6:15 
GeneralRe: My vote of 4 PinmemberSerdar Kurbanov14-May-13 8:58 
GeneralRe: My vote of 4 PinmemberSerdar Kurbanov14-May-13 9:40 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Mobile
Web01 | 2.8.140721.1 | Last Updated 14 May 2013
Article Copyright 2013 by Serdar Kurbanov
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid