Nini Manual

Nini .NET Configuration Library (http://nini.sourceforge.net/)
Copyright © 2005 Brent R. Matzelle

Table of contents: Show - Hide

1. Introduction

1.1 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. 

1.2 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:

1.3 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.

2. Getting Started

2.1 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
Language: C# only - VB only - All

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.

2.2 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. 

Language: C# only - VB only - All
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)

2.3 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:

Language: C# only - VB only - All
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

2.4 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.

Language: C# only - VB only - All
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)

2.5 Key Value Replacement

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 ReplaceKeyValues

Language: C# only - VB only - All
Here is an example in C#:
IConfigSource source = new IniConfigSource("MyApp.ini");
source.ReplaceKeyValues();
Here is the example in VB:
Dim source As New IConfigSource("MyApp.ini")
source.ReplaceKeyValues()

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

3. Advanced Topics

3.1 Merging

Merging is a very powerful functionality that allows developers to combine together the configuration data from multiple sources into a single object. You can potentially combine together an infinite number of different configuration types into a single IConfigSource! You could add multiple INI, XML, and Registry files into the same object. Pretty cool don't you think? Here is an example of how to combine an INI file with an XML file.

Language: C# only - VB only - All
Here is an example in C#:
IConfigSource mainSource = new IniConfigSource("MyApp.ini");
IConfigSource xmlSource = new XmlConfigSource("MyApp.xml");
mainSource.Merge(xmlSource);

// Now you can access any IConfig from mainSource and xmlSource
string xmlValue = mainSource.Configs["SomeXmlSection"].Get("AnOption");
Here is the example in VB:
Dim mainSource As New IniConfigSource("MyApp.ini")
Dim xmlSource As New XmlConfigSource("MyApp.xml")
mainSource.Merge(xmlSource)

' Now you can access any IConfig from mainSource and xmlSource
Dim xmlValue As String = mainSource.Configs("SomeXmlSection").Get("AnOption")

When the data is merged between files any IConfigs of the same name or containing the same keys the file being merged in will overwrite the previous ones. This is very important for those of you with clients that have different configuration needs. You can create your default configuration settings in one file and have a client specific file that will override the settings of the main file if needed. This will save you tons of work. It did for me.

3.2 Value Aliases

Many configuration files have options that are clear to programmers but very confusing to non-programmers. In order to help make a configuration file easier for non-programmers to understand a common practice is to make the keys and values read more like common human dialog.  Lets see an example of how you might return a Boolean value by using a string value that’s easier for humans to understand.  First, let’s start with the AliasExample INI file:

; AliasExample.ini
[Web Browser]
Block Popups = ON
Check For Default Browser = Off
Error Level = warn

As you can see rather than using a value like "1" or "true" for the value of each key I have used "On" and "Off", which hopefully are easier for users to understand. You will also notice that the case between each value is not entirely uppercase or lowercase. I did this on purpose to make a point. It is difficult enough to users to remember what value to place in a particular key value so to make it a bit easier on them do not make them remember what case to use as well! The problem with ignoring case is that your code would look pretty ugly as in the following example:

bool blockPopUps = (config.Get("Block Popups").ToLower() == "on");

Let's define some rules to this file to make them We want the values of the BlockPopUps section to return a Boolean value of true when the value is set to "On" and a value of false when the value is set to "Off". Furthermore, I'd like the Error Level to return the integer value of 100 when it is set to "Warn" and a value of 200 when the value is set to "Error". The following code shows how to add rules to the Alias property of the IConfigSource that defines the rules that I just defined in the previous paragraph.

Language: C# only - VB only - All
Here is an example in C#:
IConfigSource source = new IniConfigSource("AliasExample.ini");

// Creates two Boolean aliases.
source.Alias.AddAlias("On", true);
source.Alias.AddAlias("Off", false);

// Sets two integer aliases.
source.Alias.AddAlias("Error Level", "Warn",  100);
source.Alias.AddAlias("Error Level", "Error", 200);

IConfig config = source.Configs["Web Browser"];
bool blockPopUps = config.GetBoolean("BlockPopUps");
int errorCode = config.GetInt("Error Code", true);
Here is the example in VB:
Dim source As New IniConfigSource("AliasExample.ini")

' Creates two Boolean aliases.
source.Alias.AddAlias("On", True)
source.Alias.AddAlias("Off", False)

' Sets two integer aliases.
source.Alias.AddAlias("Error Level", "Warn",  100)
source.Alias.AddAlias("Error Level", "Error", 200)

Dim config As IConfig = source.Configs("Web Browser")
Dim blockPopUps = config.GetBoolean("BlockPopUps")
int errorCode = config.GetInt("Error Code", True)

The first two calls to AddAlias add Boolean values to the text “On” and “Off”.  The next two calls to this method add alias text to the “Error Level” configuration with the text of “Warn” and “Error” along with the numeric values of 100 and 200, respectively.  Next I fetched the key data.  The GetInt method is overloaded so that if the parameter is set to true then it loads the data as an alias rather than as a literal integer value.

3.3 Key Value Lists

Nini does not have a specialized method for returning lists of information. This is because there already is way to do this using a little trick with the String.Split method of the .NET Framework. Here is an INI file with a list of servers separated by the vertical line ("|") delimeter:

[MailServers]
ServerList = "http://mail.yahoo.com/|http://www.hotmail.com/|http://www.mail.com/"
Language: C# only - VB only - All
Now using the Split method we will return the list of servers as an array of strings:
string[] serverList = source.Configs["MailServers"].Get("ServerList").Split('|');
Here is the example in VB:
Dim serverList() As String = source.Configs("MailServers").Get("ServerList").Split('|')
You can use any number of delimeters with the Split method. Be creative. Just pick a delimeter that you will not be using as a key value.

3.4 Events

Nini makes things easy by allowing developers to perform operations in a disconnected fashion. A first object can usually just use an IConfig without having to worry about how other objects are using it. However, there are times when it is useful to know when the configuration data has been changed. Nini has added many events to help deal with these situations.

In the following scenario a class wants notification when an IConfigSource has been saved.

Language: C# only - VB only - All
Here is a C# example:
void SourceLoad()
{
  source = new IniConfigSource();
  source.Saved += new EventHandler(this.source_Saved);
}

void source_Saved(object sender, EventArgs e)
{
  // perform save actions here
}
Here is a VB example:
Sub SourceLoad()
{
  source = New IniConfigSource()
  source.Saved += New EventHandler(Me.source_Saved)
}

Sub source_Saved(sender As object, e As EventArgs) Handles source.Saved
{
  ' perform save actions here
}
There are more events such as Load, KeySet, KeyRemoved, ConfigAdded, and ConfigRemoved.

4. Other Configuration Types

4.1 XML Configuration

Nini has it's own XML configuration file structure. It provides more flexibility than does the .NET configuration file format. It's main advantages are that you can have more than one XML configuration file and that the format is much more concise. Here is an example of the format. You will notice that it resembles an INI file quite closely. The configuration values are the same as the INI in the previous examples:

<!-- MyApp.xml -->
<Nini>
    <Section Name="Logging">
        <Key Name="File Name" Value="MyApp.log" />
        <Key Name="MessageColumns" Value="5" />
        <Key Name="MaxFileSize" Value="40000000000000" />
    </Section>
</Nini>
Language: C# only - VB only - All

To load the file is very simple:

// Loads the XML file
XmlConfigSource source = new XmlConfigSource("MyApp.xml");
// Retrieves a value
long maxFileSize = source.Configs["Logging"].GetLong("MaxFileSize");
Here is the example in VB:
' Loads the XML file
Dim source As New XmlConfigSource("MyApp.xml")
' Retrieves a value
Dim maxFileSize As Long = source.Configs("Logging").GetLong("MaxFileSize")

4.2 Windows Registry Configuration

If you are using one of the many Microsoft Windows operating systems then you can access data from the Windows Registry. Here is an example key path for a registry item:
HKEY_LOCAL_MACHINE\Sofware\MyApp\Logging
    "File Name"       "MyApp.log"  REG_SZ
    "MessageColumns"  "5"          REG_DWORD
    "MaxFileSize"     "40000000"   REG_DWORD

To access this code the method is a bit more complex than others. You must create a mapping to a registry entry. This functionality will also give you the ability to merge many registry keys into a single IConfigSource. Here is some example code to access it.

Language: C# only - VB only - All
Here is an example in C#:
using Microsoft.Win32;
RegistryConfigSource source = new RegistryConfigSource();
// Loads the registry tree
source.AddMapping(Registry.LocalMachine, "Software\\MyApp\\Logging");
// Retrieves a value
long maxFileSize = source.Configs["Logging"].GetLong("MaxFileSize");
Here is the example in VB:
Imports Microsoft.Win32
Dim source As New RegistryConfigSource()
' Loads the registry tree
source.AddMapping(Registry.LocalMachine, "Software\\MyApp\\Logging")
' Retrieves a value
Dim maxFileSize As Long = source.Configs("Logging").GetLong("MaxFileSize")

If you'd like to recursively retrieve all data under a specified registry key there is a method to accomplish this as well.

Language: C# only - VB only - All
If you want to get all subkeys underneath a key with a flat name you can do this:
using Microsoft.Win32;
// Loads the registry tree and all nodes beneath it without 
RegistryConfigSource source = new RegistryConfigSource();
source.AddMapping(Registry.LocalMachine, "Software\\MyApp", RegistryRecurse.Flattened);

string maxFileSize = source.Configs["MyApp"].GetString("SomeConfig");
long maxFileSize = source.Configs["Logging"].GetLong("MaxFileSize");
Here is the example in VB:
Imports Microsoft.Win32
' Loads the registry tree and all nodes beneath it without 
Dim source As New RegistryConfigSource()
source.AddMapping(Registry.LocalMachine, "Software\\MyApp", RegistryRecurse.Flattened)

Dim maxFileSize As String = source.Configs("MyApp").GetString("SomeConfig");
Dim maxFileSize As Long = source.Configs("Logging").GetLong("MaxFileSize")

4.3 .NET Configuration Files

The .NET Framework has it's own configuration file mechanism that uses a specific XML format. You may be familiar with them in ASP.NET as web.config files. If you are using them with Windows Forms, console applications, or services you will know them as [APP NAME].exe.config files. To support users that still use this configuration file format in their applications Nini has support for these files as well.

<!-- ExampleApp.exe.config -->
<configuration>
    <configSections>
        <section name="Logging" type="System.Configuration.NameValueSectionHandler" />
    </configSections>
    <Logging>
        <add key="File Name" value="MyApp.log" />
        <add key="MessageColumns" value="5" />
        <add key="MaxFileSize" value="40000000000000" />
    </Logging>
</configuration>
Language: C# only - VB only - All
Accessing the data is very similar to loading an INI or XML file:
IConfigSource source = new DotNetConfigSource(DotNetConfigSource.GetFullConfigPath());

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:
Dim source As New DotNetConfigSource(DotNetConfigSource.GetFullConfigPath())

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")

4.4 Command Line (Argv) Configuration

Since the beginning of programming applications have had the capability to accept command line switches. These switches are simply strings passed into the application when it is first started. The Windows program Xcopy has many command line switches and the excellent downloading application wget has it's own switches as well. If you want to read a little more about how command line parameters work in .NET click here. Our first example is very similar to the others you have seen so far. The difference is that the AddSwitch method needs to be called for each configuration key. There is a short key and a long key that both can be used to fetch configuration data.

Language: C# only - VB only - All
Here is an example in C#:
public static int Main(string[] args)
{
   ArgvConfigSource source = new ArgvConfigSource(args);

   source.AddSwitch("Logging", "file-name", "f");
   source.AddSwitch("Logging", "columns", "c");
   source.AddSwitch("Logging", "max-file-size", "m");

   if(args.Length > 0)
   {
      string fileName = source.Configs["Logging"].Get("file-name");
      int columns = source.Configs["Logging"].GetInt("columns");
      long fileSize = source.Configs["Logging"].GetLong("max-file-size");
   }
}
Here is the example in VB:
Public Static Function Main(args() As String) As Integer
   Dim source As New ArgvConfigSource(args)

   source.AddSwitch("Logging", "file-name", "f")
   source.AddSwitch("Logging", "columns", "c")
   source.AddSwitch("Logging", "max-file-size", "m")

   If (args.Length > 0) Then
      Dim fileName As String = source.Configs("Logging").Get("file-name")
      Dim columns As Integer = source.Configs("Logging").GetInt("columns")
      Dim fileSize As Long = source.Configs("Logging").GetLong("max-file-size")
   End If
End Function

5. Using Nini Effictively

5.1 Handling configuration for multiple users

You might notice that the ConfigurationSettings class for .NET 1.0 and 1.1 only provides means of retrieving configuration values. This is because it is normally a bad idea for programmers to change configuration values for an entire application programmatically. The way the application should be globally configured is by an administrator. For this reason, I recommend that you do not change application level settings.

However, it is essential that you allow users to configure the application according to their own personal preferences. Nini allows you to create many different configuration file sources so all that you need to do is place a configuration file in the right directory. The standard for Windows programs is the application data directory:

C:\Documents and Settings\[username]\Local Settings\Application Data\[Application Name]\Settings.ini

You can get this path programatically with the following path:

string folder = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData);

5.2 Storing configuration data in a database

If you are running an ASP.NET application then you probably have multiple users in each system. You will probably also have a bunch of user settings to edit. Most web applications are entirely configured with a database so the following is an example of using Nini along with your database.

Here's a very simple example with a SQL Server table. This can easily be adapted to any other database. Here is the structure of the database table:

CREATE TABLE UserSettings
(
   UserId ID,
   Settings TEXT
);

The ConfigSettings field stores a Nini configuration value. Now you can load a Nini configuration values like this:

string userId = GetUserId(); // retrieve the user id somehow

SqlCommand command = new SqlCommand("SELECT Settings FROM UserSettings WHERE ID = "
                                     + userId, connection);

connection.Open();

SqlDataReader reader = command.ExecuteReader();

if(reader.HasRows) {
    reader.Read();
    IConfigSource source = new XmlConfigSource(new StringReader(reader.GetString(0)));
}

reader.Close();
connection.Close();

5.3 Automating configuration file creation

Being able to create builds automatically is essential for any development project. There are several tools to accomplish this such as batch (.bat) scripts, but the most popular choice for the .NET Framework is probably NAnt. You may find yourself needing to create a configuration file with your build managment system. To make these duties easier the Nini project has NiniEdit, the Nini command-line configuration editor. With this application you can create and edit any file-based configuration data. The NiniEditor is included with each Nini release in the Examples directory.

Let's add an example of how to use NiniEdit in your build. Let's assume that your build is a batch file in the first example and you need to create the following INI file programmatically:

[General]
Debug = false
Logging = On
[Logging]
FilePath = C:\temp\MyApp.log

The following calls will create the configuration file automatically:

REM Create the new configuration file
niniedit --new --set-type=ini MyApp.ini
niniedit --add=General MyApp.ini
niniedit --add=Logging MyApp.ini
niniedit --config=General --set-key=Debug,false MyApp.ini
niniedit --config=General --set-key=Logging,On MyApp.ini
niniedit --config=Logging --set-key=FilePath,C:\temp\MyApp.log MyApp.ini

If you were performing the same thing in NAnt you would do this:

<exec program="niniedit" commandline="-n -s INI MyApp.ini" />
<exec program="niniedit" commandline="-a General MyApp.ini" />
<exec program="niniedit" commandline="-a Logging MyApp.ini" />
<exec program="niniedit" commandline="-c General -k Debug,false MyApp.ini" />
<exec program="niniedit" commandline="-c General -k Logging,On MyApp.ini" />
<exec program="niniedit" commandline="-c Logging -k FilePath,C:\temp\MyApp.log MyApp.ini" />

That's all there is to it. NiniEdit has other functions such as the ability to list configs, keys, key values, and remove keys. If nothing else then use NiniEdit as an example of how to write your own command-line applications with Nini.

5.4 Creating configuration files programmatically

On occassion it might be useful to create your configuration files programmatically with your application. Doing this with Nini is very easy.

Let's say that you want to create the same INI file that you created in the either example:

[General]
Debug = false
Logging = On
[Logging]
FilePath = C:\temp\MyApp.log

Here's how you would create it in code:

IniConfigSource source = new IniConfigSource();

IConfig config = source.AddConfig("General");
config.Set("Debug", "false");
config.Set("Logging", "On");

config = source.AddConfig("Logging");
config.Set("FilePath", "C:\\temp\\MyApp.log");

source.Save("MyApp.ini");

5.5 Choosing the right configuration file type for your application

Nini was written to make all configuration file types first-class citizens. This is because each configuration file type has it's own strengths and weaknesses. The list below contains some basic guidelines:

INI XML .NET Configuration File Windows Registry

There is no perfect configuration type because each one has it's own benefits. If you end up choosing a configuration file type that turns out to not right for your situation then do not be alarmed. Nini abstracts what type of file you are accessing so the amount of code that you will have to change should be minimal.

That's it for this tutorial. I hope that you found it helpful! If you have any questions or suggestions for improving this manual then go to the Nini home page and use the forums, bug tracker, or feature request tools to make yourself heard.