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.

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.

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/"
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.

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.