TL;DR:
So, ASP.NET has a build-in method for handling custom, user-specific app configs. It’s called the ConfigurationManager, and I’ll show you how to use it.
Code is here, and the video is here (or down below, whatever floats your boat).
The ConfigurationManager
Before I show you the video, here’s what I’m doing and why. While cleaning up my RSS Aggregator project, I realized there were a lot of things I could have and should have done better. One of them being the manner in which I handled user-defined configuration data. So I set out on a quest, and these are my findings.
What it does:
In a nutshell, the ConfigurationManager creates an XML file in the bin folder. Throughout your application, you can read and write to it to save anything you might need to. You can use this to save a key-value pair and get it when you need it. In part 2.2 of my RSS Aggregator series, I use this to save user feeds and return them when I get ready to load and display the data from the RSS feeds. I pulled it out because it’s fairly complex and merited it’s own video.
Also, a small disclaimer: This tutorial is very loosely based on a Microsoft tutorial but I made several modifications; all I really needed was the code for accessing the config file.
The Config file:
This is the what and where of the file that the ConfigurationManager creates:
As you can see, there’s the exe.Config file. To the right is all of the XML data that’s contained in it. The ConfigurationManager does all of that XML stuff for us. It’s pretty nifty, eh? To abstract everything that’s going on here, I created a single Manager class that has several methods that handles everything.
The Add function:
public void Add(Item item) { if (IsUnique(item.Key)) { var config = ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None); var settings = config.AppSettings.Settings; settings.Add(item.Key, item.Data); config.Save(ConfigurationSaveMode.Modified); ConfigurationManager.RefreshSection(config.AppSettings.SectionInformation.Name); } else { throw new ConfigurationErrorsException("Item Key: " + item.Key + " is not unique"); } }
Obviously, we’re passing in an Item here. In this case, Item is just a class that has a Key and a Data property. (Just for testing, ya know)
IsUnique
is just a helper method that makes sure a key that matches the one we’re trying to create doesn’t already exist in the config file. If we had two items with the same key, the ConfigurationManager wouldn’t know which one to pick when we wanted to pull the matching data out. So, we’ll just throw a new ConfigurationErrorsException
to tell whoever is above that we couldn’t add the item because it wasn’t unique.
This is the code that tells the ConfigurationManager what to do:
//Tells the ConfigurationManager to open the config file var config = ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None); //This is just a shortcut to get to the Settings property of the config file we opened var settings = config.AppSettings.Settings; //Let's add a key and some data to the settings settings.Add(item.Key, item.Data); //Save the config file config.Save(ConfigurationSaveMode.Modified); //This tells the ConfigurationManager that the next time it accesses that Settings section, we made some changes to it and it needs to be re-read from disk ConfigurationManager.RefreshSection(config.AppSettings.SectionInformation.Name);
The Remove function:
public void Remove(string key) { var config = ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None); var settings = config.AppSettings.Settings; if (settings[key] != null) { settings.Remove(key); } else { throw new ConfigurationErrorsException(key + " does not exist or can't be read"); } config.Save(ConfigurationSaveMode.Modified); ConfigurationManager.RefreshSection(config.AppSettings.SectionInformation.Name); }
The remove function is fairly self explanatory. We’ve got the same garb as the Add function in terms of the ConfigurationManager stuff. All we’re doing here is making sure the key actually exists before we try to remove it.
The Update Function:
public void Update(Item oldItem, Item newItem) { var config = ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None); var settings = config.AppSettings.Settings; if (settings[oldItem.Key] == null) { throw new ConfigurationErrorsException("Cannot Update item " + oldItem.Key + " because it doesn't exist or can't be read"); } else if (oldItem.Key != newItem.Key) { /******************** Small Bug ******************** * Removing the item before we check to make sure it is * unique in the add function can result in a situation * in which the old item gets removed while the new item * never gets added *******************************************************/ /*Remove(oldItem.Key); //original Add(newItem);*/ Add(newItem); //Fix Remove(oldItem.Key); //Fix } else { settings[oldItem.Key].Value = newItem.Data; config.Save(ConfigurationSaveMode.Modified); ConfigurationManager.RefreshSection(config.AppSettings.SectionInformation.Name); } }
This one is a little more complex. Same ole’ ConfigurationManager stuff, but this on does a few extra things. First, we want to make sure we’re editing something that actually exists. Then, if the new key value is different than the old value, we don’t want to update anything. We just want to remove the old setting and add the new one. This won’t affect the user in this case and it let’s us reuse code we’ve already written. Now, about that gigantic comment…
When making the RSS Aggregator Part 2.2 video, I found a bug in this code. What was happening is that because I removed the old item before checking to make sure the new item was unique, a situation can occur in which the new item never gets added into the config but the old one is gone as well. Oops. By simply changing the order in which we perform these actions, this bug is fixed.
Lastly, if we are just editing the data, we want to set the value of the old key to the new data.
The Final bit:
public List List() { var items = new List(); var AppSettings = ConfigurationManager.AppSettings; foreach (var key in AppSettings.AllKeys) { var item = new Item(key, AppSettings[key]); items.Add(item); } return items; }
Last, but not least, w have the List()
function. All we’re doing is creating and returning a list of items based off the values stored in the config file. This allows us to access these items in a useful manner without having to convert them later. However, the interesting thing to note here is how we’re accessing the config file this time. We can go straight to ConfigurationManager.AppSettings
to read the keys and values because the only thing we are doing is reading from the file.
Nifty, eh?
That’s all I’ve got for this one. Stay tuned for more, and don’t forget to leave a comment either here or on the forums!