Skip to content
Christian Junk edited this page Oct 4, 2017 · 6 revisions

Table of Contents

Introduction

What is Application Configuration Data?

As a developer you deal with application configuration data all of the time. Common examples of this are INI files, XML files, .NET configuration files (aka “.config”), the Windows registry, and command line (argv) arguments. The advantages of configuration files are that they load quickly, do not take up a lot of space, and are easy to edit.

The Problem

Attempts to create configuration file access schemes do not satisfy the needs of either programmers or end-users. To give a real life scenario I worked for an organization that configured their original programs using the Windows registry API (Application Programming Interface). Later on they developed their own ASP configuration class. At about the same time another group developed an API that fetched the data from a database. Then when ASP.NET came along they started to use Web.config. In a matter of several years the number of configuration data sources grew from one to four! Needless to say getting configuration data often became a grueling task. Here are the three major areas where configuration management can be improved:

  • API
    Developers use a configuration file format that gets their application running in the shortest time possible. However the API for accessing this data is commonly added as an afterthought resulting in an inflexible API. In very small applications this might not be a problem but as a program’s codebase grows the configuration information will often find itself littered throughout the application code.

  • End Users
    Configuration files are usually not written with the end user in mind. Often the configuration options are terse programming terms that only the bravest users dare to change them. This leads to developers having to write complicated configuration file editors or worse, entirely redesigning their original APIs.

  • Multiple Configuration Sources
    As your software matures it is not uncommon for more application configuration types to be added (such as the example I gave you earlier). This often occurs because of merging code from other projects, new improved formats, and moving to different programming platforms. This forces programmers to learn multiple APIs. The end result is code that is neither consistent nor friendly to new programmers. The old configuration files aren't replaced because programmers and their managers are not comfortable with altering mature code. Users that edit the files are resistant to this change because they would prefer not to learn a new file format.

Introducing Nini

Nini is an uncommonly powerful .NET configuration library designed to help build highly configurable applications quickly. Nini provides a solution that attempts to eliminate the above problems. It provides a large feature set that gives you functionality that you will use in every phase of your project, from concept to mature product. This is accomplished through a simple, yet flexible, API that provides an abstraction over the underlying configuration sources. It solves all of the problems that I described above. We’ll see how this is done in the examples below.

Getting Started

A Simple Example

In order to show you how Nini solves these problems let’s go over an example. First, let’s take an example configuration file. I will choose the INI format for most of the examples in this manual. INI files are a tried and true configuration file type used in well known open source projects such as MySQL, PHP, and Samba. In fact, Nini has support for several INI file types. They are very simple and easy to edit so they remain a very popular choice. Nini contains it's own INI parser class ( IniDocument) which is written entirely in C# with no Windows API code so it's cross platform. Here is the text of MyApp.ini for this example:

; MyApp.ini
[Logging]
File Name = MyApp.log
MessageColumns = 5
MaxFileSize = 40000000000000

Below is a C# example piece of code that describes how to access the configuration data from the INI file from the file above:

using Nini.Config;
IConfigSource source = new IniConfigSource("MyApp.ini");

string fileName = source.Configs["Logging"].Get("File Name");
int columns = source.Configs["Logging"].GetInt("MessageColumns");
long fileSize = source.Configs["Logging"].GetLong("MaxFileSize");

Here is the example in VB:

Imports Nini.Config

Dim source As New IniConfigSource("MyApp.ini")

Dim fileName As String = source.Configs("Logging").Get("File Name")
Dim columns As Integer = source.Configs("Logging").GetInt("MessageColumns")
Dim fileSize As Long = source.Configs("Logging").GetLong("MaxFileSize")

Okay, that example threw a few things at you. First, we include Nini's configuration namespace to the imaginary app with using Nini.Config. Next we load up the INI file with the IniConfigSource class. In Nini, each configuration file type has it's own "Source" class. This class knows how to load and save the file. Each of these classes implements the IConfigSource interface so that you abstractly work with multiple configuration types more easily. When a file is loaded all sections (in this case the [Logging] section) are converted to the interface IConfig and added to a collection on the Source class. The IConfig class provides very fast access to retrieve, add, or remove configuration keys (like "File Name" in the above INI file). The methods of the IConfig class include Get, GetString, GetInt, GetFloat, GetDouble, and GetLong methods. All of the methods prefixed with "Get" are overloaded to provide more data. The next couple sections describe how to use these overloads.

Default Values

Sometimes an option will not be present in a configuration file. This might be because it hasn’t been added to the project’s main build or because it should remain secret to users. For these cases Nini provides provides overloaded methods that allow a programmer to define default values.

Here’s an example in C#:

// Sets missing to the default value, "Default result".
string missing = config.Get("Missing Config", "Default result");

// Sets smallNumber to the default value, 50.
int smallNumber = config.GetInt("Not Present", 50);

Here is the same example in VB:

' Sets missing to the default value, "Default result".
Dim missing As String = config.Get("Missing Config", "Default result")

' Sets smallNumber to the default value, 50.
Dim smallNumber As Integer = config.GetInt("Not Present", 50)

Setting, Saving, and Removing Keys

It is also possible to set and save new values into the configuration file. Calling the Set method will change an existing value or if it does not exist add it. Here is an example:

config.Set("File Name", "MyNewFile.log");
config.Set("MessageColumns", 45);
config.Remove("File Name");
    
source.Save();

It is necessary to call the Save method to save a file, h0wever, you can also set the AutoSave property on an IConfigSource and that will automatically save the file each time the Set method is called. If you want to save a document to a different path or a different object then the IniConfigSource, XmlConfigSource, and DotNetConfigSource classes all save overloaded Save methods that allow you to save to either a new path or a TextWriter:

Here is an example in C#:

using System.IO;
    
IniConfigSource source = new IniConfigSource("Test.ini");
StringWriter writer = new StringWriter();
source.Save(writer); // Save to StringWriter(TextWriter)

source.Save("some/new/path.ini"); // Save to new path

Here is the example in VB:

Imports System.IO
    
Dim source As IniConfigSource = new IniConfigSource("Test.ini")
Dim writer As New StringWriter()
source.Save(writer) ' Save to StringWriter(TextWriter)

source.Save("some/new/path.ini") ' Save to new path

Adding and Removing Configs

On occassion you will want to add and remove IConfigs yourself. Nini has a simple means to accomplish both of these actions. Here is an example where I create a new config and then immediately remove it.

Here is an example in C#:

IConfig newConfig = source.AddConfig("NewConfig");

source.Configs.Remove(newConfig);

Here is the example in VB:

Dim newConfig As IConfig = source.AddConfig("NewConfig")
    
source.Configs.Remove(newConfig)

Key Value Expanding

In many cases you will find that your key values are dependent on the values of other keys. For instance you have a root path configuration value and several values for files that use this path like in this example:

[File Path]
RootPath = C:\Program Files\My Program
Logging = MyApp.log
WebPage = index.html

Without Nini if you wanted to combine the value of "RootPath" with "Logging" and "WebPage" then you would have to perform ugly string concatenations to get "C:\Program Files\My Program\index.html". In Nini you do not need to do this:

[File Path]
RootPath = C:\Program Files\My Program
Logging = ${RootPath}\MyApp.log
WebPage = ${RootPath}\index.html

This can save you a lot of trouble concatenating them yourself and make your code a lot cleaner. If you want to grab a value from a different section you can do the same above but add the section name followed by a bar ("|") like so: ${section|key}. When you are ready to perform the replacement call ExpandKeyValues (Note: This used to be called ReplaceKeyValues)

Here is an example in C#:

IConfigSource source = new IniConfigSource("MyApp.ini");
source.ExpandKeyValues();

Here is the example in VB:

Dim source As New IConfigSource("MyApp.ini")
source.ExpandKeyValues()

When you call ExpandKeyValues it changes all of the keys in your configuration file all at once. This makes the code execution faster because it does not need to replace the key values on every Get/GetString/GetInt/etc call. However, this means that if you were to save the configuration file with the that it would overwrite your previous values with the expand values. If you do not want this to happen then you can use the GetExpanded method.

// This sets logging to "C:\Program Files\My Program\MyApp.log"
IConfigSource source = new IniConfigSource("MyApp.ini");
string logging = source.Configs["FIle Path"].GetExpanded("Logging");

That’s how easy it is to create your first Nini configured application. The following sections will explain some more advanced features of Nini.

Clone this wiki locally