A year and a half ago I wrote about my Application Configuration library that I use to handle all things related to configuration in most of my applications. The library is a code-first, class based configuration implementation where you simply create a class that inherits from a base type, add properties, and then use that class for configuration access and storage mechanism. The library provides auto-initialization and auto-creation of configuration stores. By default .NET .config files are used, but with a few lines of configuration switching providers you can easily configure providers to also use raw JSON and XML files, database records or string based storage.
This is a tool I use daily and in just about every application I built as a way to centrally store my configuration settings without having to think about how the configuration is stored. I create the class add properties as needed and the class provides the rest. Done.
In case you want to check out the project, here’s where to find more detailed info:
The latter link is a lengthy post that details the rationale behind this library as a getting started guide. The GitHub page also has similar getting started content in more direct no-nonsense fashion, but without the backstory.
When I wrote the post I had just updated the library to version 2.0, which revamped the whole implementation to make it much easier to set up from its original incarnation. I had also just moved the project out of the larger West Wind Web Toolkit into its own library and open sourced the whole shebang over to GitHub. There’s been some decent interest and feedback, and now with the version being up to 2.20 I thought it’s high time to highlight a few of the changes that were implemented since then.
Open Sourced – MIT Licensed
The original library was part of the West Wind Web Toolkit which was a (cheaply) licensed product and that caused some confusion as to the actual licensing of the library. As of mid-2013 the library is fully open source and available using the MIT license, which means you’re free to use it in just about any kind of project without any restrictions. There’s still an optional commercial license available for those that require physical licenses, feel that official personal support related to the license is required or those that simply want to support this project. Surprisingly since then quite a few people have decided to pay for a license – more so than prior to having the project under MIT. Hmmm… makes you think, huh?
Open Sourcing has been nice –there have been a few contributions and a few interesting additions in various forks. It definitely is a more interactive environment and has brought more people in to use and discuss the tool. Incidentally this was also my first public experience with Git and GitHub which has been awesome (how did I ever get along without it?)
JSON Configuration Support
A lot of people asked for JSON serialization support as an option, and originally I was somewhat resistant to it. My main reason for the resistance was primarily that it adds a dependency on a JSON parser and that that it is pretty easy to serialize to JSON manually – plus JSON as a configuration format can be troublesome because of string encoding issues. However, after getting so many requests for it I ended up adding support for it after all.
Dynamic JSON.NET
I ended creating a dynamic implementation of the JSON serialization code that uses the JSON.NET library, but doesn’t require a hard reference to JSON.NET. This means there’s no compile time dependency on JSON.NET and the NuGet package won’t explicitly pull down JSON .NET. So if you don’t use JSON serialization you don’t need to have JSON.NET in the project at runtime or even at compile time. If you want to use JSON, then just make sure JSON.NET is available by manually importing the NuGet package - or – much more likely it’ll already be part of your project anyway especially if it’s a Web project.
One thing that is nice with JSON (and also XML) is that it supports hierarchical structure, so it’s much nicer to build layer configuration objects that group related features together in child objects.
Here’s an example of a JSON configuration class which I used in a recent desktop utility application I built:
public class WebSurgeConfiguration : AppConfiguration
{
public string AppName { get; set; }
public StressTesterConfiguration StressTester {get; set;}
public UrlCaptureConfiguration UrlCapture {get; set;}
public WindowSettings WindowSettings { get; set; }
public WebSurgeConfiguration()
{
StressTester = new StressTesterConfiguration();
UrlCapture = new UrlCaptureConfiguration();
WindowSettings = new WindowSettings();
AppName = "West Wind WebSurge";
}
protected override IConfigurationProvider OnCreateDefaultProvider(string sectionName, object configData)
{
var provider = new JsonFileConfigurationProvider<WebSurgeConfiguration>()
{
JsonConfigurationFile = App.AppDataPath + "WebSurgeConfiguration.json"
};
Provider = provider;
return provider;
}
}
Next to hook up a global static reference so that the Configuration is universally accessible within the application:
public class App
{
public static WebSurgeConfiguration Configuration { get; set; }
public static string AppDataPath { get; set; }
static App()
{
AppDataPath = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData) +
"\\West Wind Technologies\\WebSurge\\";
if (!Directory.Exists(App.AppDataPath))
Directory.CreateDirectory(App.AppDataPath);
Configuration = new WebSurgeConfiguration();
Configuration.Initialize();
}
}
Note that for desktop applications, you probably want to pick your configuration file storage path carefully – here I’m using Environment.SpecialFolder.ApplicationData, which translates to the AppData/Roaming folder that can both read and write data for the local user. Don’t put config files into the application’s main folder if you plan on also writing configuration information to disk.
Adding the configuration object to a global App object is an optional step, but I use that approach for just about every application – Web, desktop or otherwise. By adding one ore more Configuration objects as statics to a ‘global’ App object, the configuration becomes globally available in the application as a Singleton, at which point I can use the configuration like this from anywhere in the application:
string name = App.Configuration.AppName;
int threads = App.Configuration.StressTester.MaxThreads;
// later maybe update and save values into the store
App.Configuration.StressTester.RunTime = 30; // secs
App.Configuration.StressTester.Threads = 8; // secs
App.Configuration.Write();
The end result is a JSON configuration file which looks something like this:
{
"AppName": "West Wind WebSurge",
"StressTester": {
"LastFileName": "C:\\Temp\\1_WebLog_Long2.txt",
"ReplaceCookieValue": null,
"MaxResponseSize": 5000,
"LastThreads": 10,
"LastSecondsToRun": 20,
"DelayTimeMs": 0,
"RandomizeRequests": true,
"RequestTimeoutMs": 20000
},
"UrlCapture": {
"IgnoreResources": true,
"CaptureDomain": "weblog.west-wind.com",
"UrlFilterExclusions": [
"analytics.com",
"google-syndication.com",
"/chrome-sync/",
"doubleclick.net",
"googleads.com"
],
"ExtensionFilterExclusions": [
".css",
".js",
".png",
".jpg",
".gif"
]
},
"WindowSettings": {
"Left": 283,
"Top": 61,
"Height": 786,
"Width": 1137,
"Split": 415
}
}
As I mentioned this is also easy enough to do manually, by creating Load() and Save() methods that handle the serialization/deserialization, but I find that using configuration class provides a common pattern for holding these configuration values consistently and potentially allowing them to be stored in a different store if necessary.
Configuration File Support for Complex Objects
As you can see in the example above, it’s pretty nice to have complex objects that nest configuration values. Unfortunately, .NET’s .config file structure doesn’t lend itself to this model very easily as the dynamic system uses key value pairs that map well to single level classes, but not so much to hierarchical data.
However, it is possible to trick the standard key value pair format into supporting simple complex objects and lists and the latest version provides some of this support. There are two ways to make this happen:
- Using custom ToString() and static FromString() methods to serialize and parse data
- Using .NET TypeConverter Attributes
Support for the latter TypeConverters has been in the tooling for a long time, but it’s a bit of a pain to use. This release however adds support for object serialization/parsing by providing a static FromString() method to deserialize and a ToString() method to serialize complex object data.
Here’s what a type that supports this interface looks like:
public class LicenseInformation
{
public string Name { get; set; }
public string Company { get; set; }
public int LicenseKey { get; set; }
public static LicenseInformation FromString(string data)
{
return StringSerializer.Deserialize<LicenseInformation>(data,",");
}
public override string ToString()
{
return StringSerializer.SerializeObject(this, ",");
}
}
Notice the static FromString() method which the .config file provider probes for if no default type can be matched. If the method exists ToString() is used to serialize and FromString() is used to create an instance of the type from a string. In the example, a helper StringSerializer class is used to handle the serialization and deserialization. This class basically iterates through all properties concatenates the values for serialization, and then pulls them back from a string when parsing. Obviously FromString() and ToString() can use whatever mechanism you chose, but StringSerializer is a quick and easy way to take an object and turn it into something simple that’s editable by hand if necessary.
To use this class in a configuration we can simply do:
public class CustomConfigFileConfiguration : AppConfiguration
{
public string ApplicationName { get; set; }
public LicenseInformation License { get; set; }
public CustomConfigFileConfiguration()
{
ApplicationName = "Configuration Tests";
License = new LicenseInformation()
{
Company = "West Wind",
Name = "Rick",
LicenseKey = 10
};
}
}
Now when this class gets serialized here’s what the configuration looks like:
<CustomConfigFileConfiguration>
<add key="ApplicationName" value="Changed" />
<add key="License" value="Rick,Updated Company,10" />
</CustomConfigFileConfiguration>
This may look somewhat silly and I agree that this shouldn’t be your primary goal in configuration design. If you truly need nested objects, it’s better to use JSON or XML and persist that way, rather than using .config file formatting. However I’ve found that I’ve needed to use this feature quite a bit recently, especially in projects that have been around a while where configuration keys are growing in size and some order is needed to keep values organized. Sometimes there are related values that are easier to edit as a single string.
This feature is also useful for the next feature enhancement which is lists where you can have multiple list items represented as nested string values.
Configuration File support for IList
Another feature that creeps up from time to time is the ability to display multiple items of the same type – a list essentially. In a recent project we needed to configure multiple queue servers for startup with each server with its own three or four settings. The ConfigurationFileConfigurationProvider serializes and parses IList based values by iterating over each item and serializing it using the string parsing routines used for single elements. This means you can serialize both simple values like lists of strings, or complex values like the LicenseInformation type above as long as it supports either a TypeConverter or the FromString()/ToString() combination of methods.
Here’s an example of a class that has a list of string values:
public CustomConfigFileConfiguration()
{
ApplicationName = "Configuration Tests";
ServerList = new List<string>()
{
"DevServer",
"Maximus",
"Tempest"
};
}
When this list gets serialized it now shows up as the following configuration XML:
<CustomConfigFileConfiguration>
<add key="ApplicationName" value="Changed" />
<add key="ServerList1" value="DevServer" />
<add key="ServerList2" value="Maximus" />
<add key="ServerList3" value="Tempest" />
</CustomConfigFileConfiguration>
Each item is serialized using the name of the property plus a one based index. Likewise the parser reads the list and checks for values starting with 1 and upwards until a value comes back null to retrieve the values.
It also works with complex values – here’s an example of a QueueConfiguration that has a list type that contains each queue configuration which holds the name of the queue, the number of processing threads and a wait interval:
<QueueControllerConfiguration>
<add key="ServerHostAddress" value="http://*:8081/" />
<add key="Queue1" value="MPWF,5,100" />
<add key="Queue2" value="MPWFORDERS,4,5000" />
<add key="Queue3" value="MPWFEVENTS,2,1000" />
</QueueControllerConfiguration>
Again this isn’t the cleanest design, but as is often the case when we started out we didn’t anticipate the need to have multiple queue configurations. This requirement came up much later when configuration settings and management through deployment targets had already been set and it was simply easier to shoehorn these queue settings into the existing .config file format we’d been using.
Both the complex type and IList support have proven very useful on a few occasions and although I don’t recommend them in your primary configuration layout, they can definitely be a life saver when you need to add settings that require more complexity in a hurry. If you have the option of re-design or starting from scratch and you find yourself adding complex objects or lists, then you’ll probably much happier with using a serialization format like JSON or XML that supports hierarchical layouts more naturally.
Summary
I’m glad to see this library is getting more use since it’s been rescued from relative obscurity in the West Wind West Wind Toolkit into it’s own more focused library. I’m starting to regret past decisions of building larger toolkits that included a hodgepodge of features – clearly it’s much better to have small but very focused tools that have appeal.
I hope this configuration class is useful to some of you as it has been to me.
Resources
Other Posts you might also like