Application Configuration++
What's the one thing your dad told you never to do as a developer? That's right, he said "son or daughter (perhaps he wasn't sure), you should never ever hard code your variables." What happens when you hard code that domain name in your method? You end up doing a full release just to update from "domain1.com" to "domain2.com" and as you know every new deployment includes the risk of new bugs. Imagine doing a full release to change a single domain name only to find out that you've introduced a bug that brought down the entire application...not good for business or job security.
To help .Net developers avoid this pitfall (not as fun as Atari made it out to be) Microsoft included the App.config with .Net 1.0 for you to use in your desktop applications (Web.config for web applications). When you create a Windows application in Visual Studio, App.config is automatically generated and included in your solution. When you build your application, the App.config is automatically included in the output with the appropriate name [your app executable name + .config]. You can include configurable values within the App.config and gain access to them in your code using the ConfigurationManager class.
Here is an example of an App.config...
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<appSettings>
<add key="Domain" value="MyDomain.com"/>
</appSettings>
</configuration>
As you can see the basic App.config provides a very basic structure for storing and retrieving configuration data using key/value pairs. There are some other sections that App.config includes, but they're not important for this article. However, I recommend that you experiment with them to see what value they can add to your projects.
And here's an example of how to access the Domain configuration key value...
Note: Don't forget to include the System.Configuration assembly in your references.
using System;
using System.Configuration;
namespace SampleApplication
{
class Program
{
static void Main(string[] args)
{
string domain = ConfigurationManager.AppSettings["Domain"];
Console.WriteLine(domain);
}
}
}
However, this article isn't about just using the built-in configuration functionality. No sir, that's day one stuff. In this article I'll show you how to take configurations to the next level (mind you not the top level but a decent step up from beginner level). I'll show you how to combine the built-in configuration functionality with custom configuration sections and custom environment variables to ensure you never take down your application by introducing a bug just to change a domain name variable again (or ever if you've been lucky so far). Finally, as I always love to do, I'll show you how to tie the entire bit of code together using an Extension which will allow you to reuse your code anywhere. Enough with the pleasantries, let's get started.
Custom Configuration Sections
First consider what you might do when presented with the following question from business...
Hey Jim (your name is now Jim), sometimes we need to access the Production domain and sometimes we need to access the Test domain. How does your app handle that?
On first consideration you may want to just add to the appSettings section a couple of new keys, one called Production that points to the production domain and one called Test that points to the test domain. What happens when more domains are requested later (and you know they will)? So this is not a good solution because it doesn't scale. A better solution is to add a custom configuration section that contains the Production and Test domains, as well as any other domains that business will inevitably want to add, then create a single Domain key in the appSettings section as a pointer to the correct domain (or perhaps you can use some other designator in code to determine which domain to use).
Of course, this will also require that you include a custom section in the ConfigurationSections of the App.config. Here's what the new App.config will look like...
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<configSections>
<section name="domainSettings" type="System.Configuration.DictionarySectionHandler"/>
</configSections>
<appSettings>
<add key="Domain" value="Production"/>
</appSettings>
<domainSettings>
<add key="Production" value="Production.com"/>
<add key="Test" value="Test.com"/>
</domainSettings>
</configuration>
Let's break this new App.config down into its separate parts. First, the appSettings key named Domain which previously contained the single domain has been modified so that it now points to the appropriate domain for the environment (in this example it's named Production). Second, a new section named domainSettings has been added which contains the Production domain and the Test domain. Finally, to access the new domainSettings section the configSections section has been added and includes a domainSettings section so the ConfigurationManager class can correctly determine the type of the section. As you can see this type is the normal System.Configuration.DictionarySectionHandler which is the same as the appSettings type (it's a simple key/value type).
Note: I highly recommend you look into custom configuration sections in more detail. You can use this customizability of .Net to contain and manage all maner and structure of configuration data. Imagine using your own custom classes to contain custom settings for your application. Look at some of my other articles to see how to do this.
Here's how you access the new domainSettings section...
using System;
using System.Configuration;
namespace SampleApplication
{
class Program
{
static void Main(string[] args)
{
string domain = ConfigurationManager.AppSettings["Domain"];
Console.WriteLine(
(ConfigurationManager.GetSection("domainSettings") as System.Collections.Hashtable)[domain]
);
}
}
}
As you can see the application still has the string variable named domain but it now contains the pointer to the appropriate domainSettings section key. You can access this new section using the ConfigurationManager.GetSection method. This returns an object because a section could be anything so you have to convert it to a System.Collections.Hashtable. Once you've done that you can access the appropriate key using the domain variable that you previously pulled from the appSettings. You now have access to the domain without using hard coded values, but why stop there? Why should you have to go through these steps every time you want to access the domain value? You shouldn't of course, because you're a lazy developer and hate writing code more than once.
Custom Environment Variables
To avoid having to access the App.config every time you want to use the domain, you can add the domain to the Environment variables. As you may or may not know, environment variables are as old as DOS and can be any key/value pair you like. Open a Command Prompt, type 'set' and you'll see the current environment variables loaded by the system. In .Net you can access these same variables using the Environment class. Fortunately for lazy developers you can use this same Environment class to add variables to the environment (only accessible within your application).
Here's how you can add the domain to the Environment variables...
using System;
using System.Configuration;
namespace SampleApplication
{
class Program
{
static void Main(string[] args)
{
string domain = ConfigurationManager.AppSettings["Domain"];
Environment.SetEnvironmentVariable("DOMAIN",
(ConfigurationManager.GetSection("domainSettings") as System.Collections.Hashtable)[domain].ToString()
);
Console.WriteLine(Environment.ExpandEnvironmentVariables("The domain is %DOMAIN%"));
}
}
}
The Environment.SetEnvironmentVariable method lets you add variables to the Environment by adding a key name and a value. So, you can easily use this method to add your domain value to the environments for your application. You can use GetEnvironmentVariable to pull variables back out, but I've chosen to demonstrate using the ExpandEnvironmentVariables because I believe it's much more valuable and demonstrates the real value of environment variables.
But why stop there? You've customized the config to handle multiple domains and added the current domain to the environment variables for easy access. That' all well and good, but wouldn't you have to type the Environment.ExpandEnvironmentVariables every time you wanted to use the domain (or any other environment variable)? That's a lot of characters to be typing all over your application's code. No lazy developer wants to do that. To avoid all that typing you can use Extensions to shorten Environment.ExpandEnvironmentVariables down to just Expand(). Let's see how to do that.
Custom Extensions
Starting in .Net (some past version I'm can't remember and don't want to look it up) Microsoft included custom extensions. Simply put, custom extensions are extensions to objects which you can create and use to provide custom functionality on the object to which you call the extension on. For example, you can create a custom extension to the String object called Expand which performs the Environment.ExpandEnvironmentVariables functionality on the current string. Where you would normally have to type...
Environment.ExpandEnvironmentVariables("The domain is %DOMAIN%")
With extensions you would only have to type...
"The domain is %DOMAIN%".Expand()
And think about all the keystrokes you'll save.
Here's how to create a custom extension for the String object to handle expanding the environment variables...
public static class Extensions
{
public static string Expand(this string val)
{
return Environment.ExpandEnvironmentVariables(val);
}
}
Note: you don't want to just include this Extensions class in your current namespace. Instead you're better off creating a separate project that contains all of your extensions which you can include as a reference in any project, for maximum laziness and reusability. To do that you'll need to use the using keyword.
Here is the final version of your code including a sperate class for the Extensions class...
The App.config...
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<configSections>
<section name="domainSettings" type="System.Configuration.DictionarySectionHandler"/>
</configSections>
<appSettings>
<add key="Domain" value="Production"/>
</appSettings>
<domainSettings>
<add key="Production" value="Production.com"/>
<add key="Test" value="Test.com"/>
</domainSettings>
</configuration>
The Extensions class...
using System;
namespace Extensions
{
public static class StringExts
{
public static string Expand(this string val)
{
return Environment.ExpandEnvironmentVariables(val);
}
}
}
And the main app (notice the 'using Extensions' to include the Extensions class)...
using System;
using System.Configuration;
using Extensions;
namespace SampleApplication
{
class Program
{
static void Main(string[] args)
{
string domain = ConfigurationManager.AppSettings["Domain"];
Environment.SetEnvironmentVariable("DOMAIN",
(ConfigurationManager.GetSection("domainSettings") as System.Collections.Hashtable)[domain].ToString()
);
Console.WriteLine("The domain is %DOMAIN%".Expand());
}
}
}
Now go write reusable code and be as lazy (or as we tell business 'efficient') as you can be.