Tag Archives: AppSettings

C# AppSettings: Upgrading Settings between Assembly Versions

This article was originally posted at Programmer’s Ranch on 15th April 2014.

In “C# AppSettings: Saving User Preferences in a WPF Browser application“, we learned how to define and use user settings in a .NET application. In this article we’ll deal with a slight complication when it comes to maintaining settings between different application versions. But first, let’s build a very simple application to get the point across.

After creating a new Console Application in your favourite IDE, go Add -> New Item… and add a Settings file called… well… Settings.settings! In the designer, add a setting called Name which will be a String in User scope:

cs-settingsupgrade-designer

In our Main() method, let us now write some code that does something with this setting.

        public static void Main(string[] args)
        {
            string name = Settings.Default.Name;
           
            if (string.IsNullOrEmpty(name))
            {
                Console.WriteLine("Hello! Who might you be?");
                Console.Write("You say: ");
                name = Console.ReadLine();
                Console.WriteLine("Pleased to meet you, {0}!", name);
               
                Settings.Default.Name = name;
                Settings.Default.Save();
            }
            else
            {
                Console.WriteLine("Hi {0}! Nice to see you again!", name);
            }
           
            Console.ReadKey(true);
        }

We’re not doing anything fancy here. If the Name setting contains something, then we just output a one-liner; otherwise we ask the user for his name and then save it in the Name setting. Here’s what it looks like when you run it for the first time:

cs-settingsupgrade-introductions

And when we run it again:

cs-settingsupgrade-remember

Now you see, these settings are tied to the version of the program. Each project you compile has something called an assembly version. This is an internal version number assigned to each .exe and .dll file. In SharpDevelop, by default this is set to 1.0.*, which generates something like 1.0.5218.35490 (the actual value is different each time you build, because of the ‘*’ bit). You can find the AssemblyVersion within the AssemblyInfo.cs file that is automatically created when you create a new project:

cs-settingsupgrade-assemblyversion

Now, since a different assembly version is generated with each build, you’ll be quick to notice a problem. If you change the code (even just add a space) and build and run again, the setting you had earlier is somehow lost, and you’re back to the name prompt:

cs-settingsupgrade-introduction-again

The problem here is that the settings are tied not only to program and user, but also to the assembly version. Each time the assembly version changes, a new set of settings are created for the application.

We can use a fixed assembly version:

[assembly: AssemblyVersion("1.0.0.0")]

…and now even after changing code, the settings are remembered:

cs-settingsupgrade-remember-again

However, the problem remains. At some point we’ll want to change that assembly version to, say, 1.0.0.1, and we don’t want to lose our settings.

There is actually a solution to this problem, described in this StackOverflow answer. In a nutshell, you need to import the settings from your old application version by upgrading them.

To do this, first add a boolean setting called UpgradeRequired in your settings file, and make sure to set it to True by default:

cs-settingsupgrade-upgraderequired

Before we actually use this, let’s change the assembly version to see that the settings are not loaded:

[assembly: AssemblyVersion("1.0.0.1")]

And sure enough, you are asked for your name once again when running the program:

cs-settingsupgrade-introduction-3

Then, at the beginning of Main(), add the following code to upgrade the existing settings:

            if (Settings.Default.UpgradeRequired)
            {
                Settings.Default.Upgrade();
                Settings.Default.UpgradeRequired = false;
                Settings.Default.Save();
            }

If you now run the application, the old settings are retrieved and saved into the new settings. Note that the UpgradeRequired setting is set to false as part of the upgrade operation, so that it is done only the first time.

cs-settingsupgrade-remember-again

It works pretty nicely.

In this article, we learned about the assembly version, an internal version associated with each .dll and .exe file produced when compiling a .NET project. We also learned about the role it plays in .NET user settings, and how to upgrade them to make them survive changes in the assembly version.

C# AppSettings: Saving User Preferences in a WPF Browser application

This article was originally posted at Programmer’s Ranch on 23rd March 2014.

Hey Ya! 🙂

In yesterday’s article, “C# AppSettings: Launching Programs and Storing AppSettings“, we learned how you can store application settings in an App.config file so that they can be easily changed without having to recompile the program. This is useful for settings that don’t change very often, but is not the best way to store user preferences.

In today’s article, we will see how we can use a .NET Settings file in order to save user preferences. To do this, we will build a simple WPF browser application* and allow the user to save his homepage.

* For those who already know a little bit about WPF, we’re just going to make a simple application that includes a WebBrowser control – not an XBAP.

So, for starters, create a new WPF application using your favourite IDE. Aside from an App.xaml, you should also have a window – this would be Window1.xaml in SharpDevelop, or MainWindow.xaml in Visual Studio.

Change the window’s TitleWidth and Height properties to something decent (e.g. width of 800 and height of 600). Then, replace the default <Grid> in the window’s XAML with the following:

    <DockPanel>
        <DockPanel LastChildFill="True" DockPanel.Dock="Top">
            <Button Name="GoButton" DockPanel.Dock="Right" Margin="5 5 5 5" Click="Button_Click">Go</Button>
            <Button Name="SaveButton" DockPanel.Dock="Right" Margin="5 5 5 5">Save homepage</Button>
            <TextBox Name="Homepage" DockPanel.Dock="Left" Margin="5 5 5 5" />
        </DockPanel>
        <WebBrowser Name="Browser" VerticalAlignment="Stretch" />
    </DockPanel>

This isn’t a WPF tutorial so don’t worry about the above XAML; you’ll see what it does in a second. Find the window’s codebehind file by expanding your Window1.xaml (or MainWindow.xaml) node in Solution Explorer, and open it:

csbrowser-codebehind

In the window class’s constructor, add the following code after InitializeComponent():

            string homePageUrl = "http://en.wikipedia.org/wiki/Main_Page";
            this.Homepage.Text = homePageUrl;
            this.Browser.Navigate(homePageUrl);

You can now run the application to see what it looks like:

csbrowser-browser

That was pretty easy! We set up the browser embedded in our window to load Wikipedia by default, and that’s what it did. Now, let’s get our program to actually do something.

In the XAML for the “Go” button, start typing the word “Click”, then press TAB twice. This should generate the following code in your window’s codebehind file (remember, it’s the .xaml.cs file):

        void GoButton_Click(object sender, System.Windows.RoutedEventArgs e)
        {
            throw new NotImplementedException();
        }

Replace the body of this method with the following:

this.Browser.Navigate(this.Homepage.Text);

We can now type in a URL and press the “Go” button to navigate there:

csbrowser-navigate

Now, let’s work on that “Save homepage” button. First, add a button handler as you did before – by starting to type the word “Click” in the XAML for the “Save homepage” button, pressing TAB, selecting “<new event handler>”, and then pressing TAB again. You should have an empty handler similar to the one you had for the “Go” button earlier:

        void SaveButton_Click(object sender, System.Windows.RoutedEventArgs e)
        {
            throw new NotImplementedException();
        }

Next, right click on your project in Solution Explorer, and select Add -> New Item… and add a new Settings file. If you’re using SharpDevelop, you’ll find this under the Misc category:

csbrowser-addsettingsfile

This file opens up a table where you can add settings as you like. Let’s add one for the homepage:

csbrowser-editsettingsfile

You just need to set the name, data type, and default value of the property. The Scope can be either User or Application. This article at MSDN explains the difference:

Application-scope settings are read only, and can only be changed at design time or by altering the <AssemblyName>.exe.config file in between application sessions. User-scope settings, however, can be written at run time, just as you would change any property value. The new value persists for the duration of the application session. You can persist changes to user settings between application sessions by calling the Settings.Save method. These settings are saved in the User.config file.

In short, Application-scope settings are pretty similar to what we did yesterday in “C# AppSettings: Launching Programs and Storing AppSettings“, while User-scope settings are ideal for saving user preferences – which is what we want to do now.

In your window’s constructor, you can now load the homepage from your settings. You first need to build your project to allow the IDE to do some magic underneath (otherwise you won’t be able to find the property. Then, you can load the homepage setting like this:

            string homePageUrl = Settings1.Default.HomePage;

The good thing about these kinds of settings are that they are strongly-typed. This means that if you’re loading, for example, an integer, you can just assign it directly to an integer variable without further ado. Using regular AppSettings (see “C# AppSettings: Launching Programs and Storing AppSettings“), on the other hand, you first have to read values into strings and then convert them to the appropriate type.

Saving settings is also pretty straightforward. You assign them using the same notation as above, and then Save() them so that the settings can be remembered next time you run the application:

        void SaveButton_Click(object sender, System.Windows.RoutedEventArgs e)
        {
            Settings1.Default.HomePage = this.Homepage.Text;
            Settings1.Default.Save();
           
            MessageBox.Show("Settings have been saved", "Homepage update");
        }

So now, we can run our application, enter a new URL in the homepage, and click the “Save homepage” button:

csbrowser-savesettings

…and the next time we run our application, it loads our new homepage by default:

csbrowser-loadsettings

As you can see, the WPF WebBrowser control is actually some version of Internet Explorer – you can see it compared to a REAL browser in a previous screenshot. Still, Internet Explorer can sometimes be useful. To download another browser, for instance. 😀

Cool. 🙂 In this article, we saw how to use a .NET Settings file to load and save user preferences quickly and easily. We saw this in action by loading and saving the homepage in a simple WPF browser application. Thanks for reading, and see you in time for the next article! 🙂

C# AppSettings: Launching Programs and Storing AppSettings

This article was originally posted at Programmer’s Ranch on 22nd March 2014.

Hi all! 🙂

Today we’re going to write a little program that can start other programs. For example, it might launch MS Word, or your favourite browser. From this we will learn how to actually start other programs using C#, and also how to add settings into an application configuration file so that they can easily be changed.

Start off by creating a new Console Application using your favourite IDE. Throw out any code already in Main(), and add this initial code:

Console.Title = "Launcher";

// TODO code goes here

Console.ReadLine();

Now, to start a program from our code, we need to use the Process class. To be able to use it, we need to add the following to our usings:

using System.Diagnostics;

Starting a program is really easy. All we need to do is replace our “TODO” comment with the following:

string filePath = @"C:\tools\notepad++\notepad++.exe";
Process.Start(filePath);

And sure enough, when we run our program, Notepad++ is opened as well:

csappsettings-notepad++

Let us now take this a step further. We can allow the user to type in the name of an application, and the corresponding application will be launched. We could do this using a switch statement as in “C# Basics: Command Interpreter Using Methods“. Or even better, we can use a Dictionary to store the relationship between application names and their path. First, add the following using at the top if it’s not there already (e.g. if you’re using SharpDevelop):

using System.Collections.Generic;

Next, our Dictionary, using the collection initialiser syntax as in “Morse Code Converter Using Dictionaries“:

            Dictionary<string, string> programs = new Dictionary<string, string>()
            {
                { "notepad++", @"C:\tools\notepad++\notepad++.exe" },
                { "firefox", @"C:\tools\firefox\firefox.exe" }
            };

We can now accept user input and launch the appropriate program. Replace the two lines we wrote earlier (which include the call to Process.Start()) with this:

            Console.Write("Enter program to launch: ");
            string programName = Console.ReadLine();
            if (programs.ContainsKey(programName))
            {
                string path = programs[programName];
                Process.Start(path);
                Console.WriteLine("{0} launched from {1}", programName, path);
            }
            else
                Console.WriteLine("Unknown program");

The user gives us the name of the program he wants to launch. If that name exists in our dictionary, we get the corresponding path, and then launch it as we did before. If we try this now, it works pretty nicely:

csappsettings-firefox

Although this works pretty well, it isn’t very flexible to have the program names hardcoded in your program code. Imagine you give this program to a friend. It wouldn’t be very useful if he could only launch Firefox and Notepad++. What if he wanted to be able to launch SharpDevelop as well? And maybe in future he might want to add VLC as well. It obviously isn’t convenient for you to have to change the code, recompile, and give him an updated program each time.
One option could be to store these program settings in a file and read it, but .NET provides us with a simpler option that exists specifically for this kind of scenario. .NET applications can optionally come with an application configuration file, which is usually called App.config. In fact, if, like me, you’re using SharpDevelop 5 Beta 2, you should have one already (otherwise just add an Application Configuration File to your project):

csappsettings-appconfig

When you build the application, App.config gets renamed to the name of the executable, plus a “.config”, and is stored in the same folder:

csappsettings-appconfig2

In this App.config file, under the <configuration> node, we can add an <appSettings> node and store our settings. The settings have a key, which is a name that identifies them, and a value which is the value of the setting. Ours will be like this:

    <appSettings>
        <add key="notepad++" value="C:\tools\notepad++\notepad++.exe" />
        <add key="firefox" value="C:\tools\firefox\firefox.exe" />
    </appSettings>

With this done, we can now rewrite our program to use these settings. First, we need to add a reference to System.Configuration. To do this, right click on the name of the project in Solution Explorer, and select “Add Reference”:

csappsettings-addreference1

In the “Add Reference” window, locate “System.Configuration” and then click “OK”.

Next, add the following using statement at the top:

using System.Configuration;

We can now use ConfigurationManager to obtain our app settings, like this:

        public static void Main(string[] args)
        {
            Console.Title = "Launcher";
           
            string firefoxPath = ConfigurationManager.AppSettings["firefox"];
            Console.WriteLine(firefoxPath);
           
            Console.ReadLine();
        }

…And here’s what we see when we run this:

csappsettings-read-appsettings

So now, all we need to do is let the user tell us which program he wants to launch, and find it in the AppSettings:

        public static void Main(string[] args)
        {
            Console.Title = "Launcher";
           
            Console.Write("Enter program to launch: ");
            string programName = Console.ReadLine();
           
            string path = ConfigurationManager.AppSettings[programName];
            if (path == null)
                Console.WriteLine("Unknown program.");
            else
            {
                Process.Start(path);
                Console.WriteLine("{0} launched from {1}", programName, path);
            }
           
            Console.ReadLine();
        }

The only thing worth noting here is that if the specified key does not exist in the AppSettings, retrieving that key via ConfigurationManager.AppSettings[] returns null, allowing us to take appropriate action in that case.

So now, when you give this program to a friend, he can just add the following line in the CsAppSettings.exe.config file that conveniently comes with the program:

        <add key="fiddler" value="C:\tools\fiddler2\Fiddler.exe" />

…and without further ado, he can now launch Fiddler as well:

csappsettings-fiddler

Wonderful! 🙂

In this article, we learned how to start another program from our code, and we also learned how to work with application settings in an application configuration file.

Storing your application settings in an App.config file is convenient because they can be tweaked as needed, without having to recompile your program. These kinds of settings are best suited for settings that don’t change frequently, so you just set them when you need them, and forget about them. In the next article, we’ll learn about another kind of settings that are great for storing stuff like user preferences which can potentially change a lot.

Transforming Config Files and XML Documents

When you’re deploying an application to different servers representing different environments, manually updating configuration settings can get very messy. In .NET applications, one of the ways to keep configuration settings under control is by using configuration transforms.

These transforms are a set of rules that define how the App.config or Web.config is to change (e.g. add appsettings, replace connection strings, etc) for the particular environment.

Syntax

Typically, when you create a new web application in Visual Studio, you’ll get a little more than just a Web.config file. In fact, if you expand the Web.config node in Solution Explorer, you’ll find that there are another two files: Web.Debug.config and Web.Release.config.

config-transform-web-default

As shown in the screenshot above, the Web.Release.config has a similar structure to Web.config itself; however it defines rules that work on the Web.config to produce a different result. In this case, there is a transform that will remove the debug attribute from the compilation node. You can see the result of this if you right click on Web.Release.config and select Preview Transform:

config-transform-preview

One of the most common places where transforms come in handy is to change the value of appsettings when deploying. Let’s say, for example, that we have the following setting in our App.config or Web.config file:

  <appSettings>
    <add key="SomeFilePath" value="myfile.txt" />
  </appSettings>

In our transform file (which so far has been Web.Release.config), we could define the following transform:

  <appSettings>
    <add key="SomeFilePath" value="C:\data\productionfile.txt"
         xdt:Transform="Replace"
         xdt:Locator="Match(key)" />
  </appSettings>

This tells the transform engine to look for the matching key (in this case “SomeFilePath”), and replace it with what is specified in the transform file:

config-transform-preview2

Similarly, it is very common to replace database connection strings depending on the environment. In fact, the default Web.Debug.config and Web.Release.config (shown earlier) explain in comments how to do this.

Other than removing attributes and replacing values, transforms support various other operations which allow insertion and removal of nodes and attributes. See Web.config Transformation Syntax for Web Project Deployment Using Visual Studio (official documentation) for detail.

Despite the title of that article, transforms may also be used with App.config files (for non-web projects) and just about any arbitrary XML file (although not quite in the same way as shown above). For web applications, Visual Studio provides some support for creating and previewing transforms. For other application types and XML documents, it is necessary to resort to other tools.

Tools

One of the simplest ways to apply config transforms (in particular, the Release transform) is to Publish your web application. This will compile your web application in Release mode, apply config transforms, and put it in the destination you specify.

Unfortunately, this approach has several drawbacks:

  • It is only suitable for web applications (not e.g. console applications).
  • It can’t be used for arbitrary XML files.
  • It can’t be extended to various different environments.
  • Being embedded in Visual Studio, it can’t be automated or integrated with a continuous integration system.

In recent years, Microsoft released an XML Document Transformation (XDT) library that enabled a variety of tools to run these transforms. This is available as the Microsoft.Web.Xdt NuGet package, and the source code is available on CodePlex.

The Web.config Transformation Tester is a web application that is useful for testing transforms. It uses XDT internally (see its source code) and can transform web and application configs as well as arbitrary XML files.

Config Transformation Tool (ctt) or XDT Transformation Tool is a command-line utility launched in 2010 also based on Microsoft’s XDT library. Although its Codeplex site says it’s still Alpha, I’ve used it to transform Web.config, App.config and XML files.

Say we have this Web.config file:

<?xml version="1.0"?>
<configuration>
  <appsettings></appsettings>
  <connectionStrings>
    <add name="foo" connectionString="value"/>
  </connectionStrings>
  <system.web>
    <customErrors mode="Off"/>
  </system.web>
</configuration>

…and we have this transform file called Web.Debug.config:

<?xml version="1.0"?>
<configuration xmlns:xdt="http://schemas.microsoft.com/XML-Document-Transform">
  <appsettings>
    <add key="EnvironmentName" value="Debug"
        xdt:Transform="Insert" />
  </appsettings>
  <connectionStrings>
    <add name="foo" connectionString="differentValue"
        xdt:Transform="Replace" xdt:Locator="Match(name)" />
  </connectionStrings>
</configuration>

Then we can run ctt as follows:

ctt s:Web.config t:Web.Debug.config d:Web.Out.config i

I’m using the shorthand parameters here:

  • s: marks the source file.
  • t: marks the transform file.
  • d: marks the destination file.
  • i causes the output to be indented, because by default it’s minified and unreadable.

The output is as follows:

<?xml version="1.0"?><configuration>
    <appsettings>
        <add key="EnvironmentName" value="Debug" />
    </appsettings>
    <connectionStrings>
        <add name="foo" connectionString="differentValue" />
    </connectionStrings>
    <system.web>
        <customErrors mode="Off" />
    </system.web>
</configuration>

This also works on XML files. In fact, I’ve taken Microsoft’s sample books.xml file and applied the following transform file (books.transform.xml):

<?xml version="1.0"?>
<catalog xmlns:xdt="http://schemas.microsoft.com/XML-Document-Transform">
   <book id="bk113" xdt:Transform="Insert">
      <author>David Kushner</author>
      <title>Masters of Doom</title>
      <genre>Non-Fiction</genre>
      <price>10.95</price>
      <publish_date>2003-01-01</publish_date>
      <description>How two guys created an empire and transformed pop culture</description>
   </book>
</catalog>

This command applies the transformation:

ctt s:books.xml t:books.transform.xml d:books.out.xml i

As you can see, the insertion transform actually works:

config-transform-ctt-xml

Additionally, as a command line tool, ctt is great to automate transforms as part of a deploy script (e.g. in a batch file).

Aside from ctt, XDT’s open sourcing announcement lists a couple of other tools that use Microsoft’s XDT library. One is a Web Config Transform Runner which I haven’t tried but which is presumably similar to ctt. The other is SlowCheetah, a Visual Studio Extension which allows transforms to be previewed and added. The author discontinued development on SlowCheetah (see also Github thread) out of frustration that it wouldn’t become a first class citizen in Visual Studio, which ironically is what happened because the preview shown at the beginning of this article was created with Visual Studio 2015 without SlowCheetah installed.

Finally, this article would not be complete without a mention of TeamCity. Automating transformation of configuration settings is only one step in what should be a complete continuous integration solution running automated builds. TeamCity is just that, and it has support for transforms.

A Framework for Application Settings

It is a very common practice to store settings in config keys within the AppSettings section of an App.config file. These settings then need to be read and converted to the appropriate type. One must also take care to cater for situations where the key is not found, or the value is invalid. This article provides a structured approach to this practice. Feel free to review and use the accompanying source code.

Update 2015-02-28: I made a minor improvement to ReadAsync() suggested by Stephen Cleary, who I thank for the code review.

Update 2015-03-03: Some people have asked why we actually need AppSettings any more, given that there are alternatives such as .NET Settings or custom configuration sections. They are correct. However I still see a lot of projects using AppSettings, and this article is intended to provide a better way to deal with those AppSettings.

Update 2015-11-12: If you want to use this in your own code, check out my .NET Settings Framework project which is based on this article and provides NuGet packages that you can just drop into your projects.

The Problem

I’ve seen a lot of production code that reads values from config keys in App.config that looks something like this:

            // set a default, just in case the key is not found or the conversion fails

            int timeout = 3000;

            // retrieve the value for the desired key

            string timeoutStr = ConfigurationManager.AppSettings["timeoutInMilliseconds"];

            // check whether the key was actually found; if not, the default value is retained

            if (timeoutStr != null)
            {
                // attempt to convert to the desired type
                //   -> if it succeeds, the default value is replaced with the retrieved value
                //   -> if it fails, the default value is retained

                bool converted = int.TryParse(timeoutStr, out timeout);
            }

Aside from the bloat due to comments and braces (which were both necessary to make this example clear), you can see that we essentially have four lines of code just to read an integer setting from App.config.

What’s really bad is that there will essentially be four lines of code for every setting, all doing essentially the same thing for different settings. That isn’t very DRY.

A Basic Solution

One of my earlier attempts at solving this problem involved a utility class which took care of reading the settings and converting them to the appropriate type, using a specific method per type:

    public class ConfigKey
    {
        private string key;

        public ConfigKey(string key)
        {
            this.key = key;
        }

        public int GetAsInt(int defaultValue = 0)
        {
            int value = defaultValue;

            string valueStr = ConfigurationManager.AppSettings[this.key];

            if (valueStr != null)
            {
                bool converted = int.TryParse(valueStr, out value);
            }

            return value;
        }

        public bool GetAsBool(bool defaultValue = false)
        {
            bool value = defaultValue;

            string valueStr = ConfigurationManager.AppSettings[this.key];

            if (valueStr != null)
            {
                bool converted = bool.TryParse(valueStr, out value);
            }

            return value;
        }

        // ...
    }

This approach was pretty decent, as it made it very easy to read settings and specify optional default values:

            int timeout = new ConfigKey("timeoutInMilliseconds").GetAsInt(3000);
            bool enabled = new ConfigKey("enabled").GetAsBool(false);

The only problem with this class is that while removes the bloat and duplication from the actual logic, it is full of duplication itself: you need a method per type to perform the type-specific conversion.

A Generic Approach

The duplication in the ConfigKey class is solved by using a generic conversion method:

        public T Get<T>(T defaultValue = default(T)) where T : IConvertible
        {
            T value = defaultValue;

            string valueStr = ConfigurationManager.AppSettings[this.key];

            if (valueStr != null)
            {
                try
                {
                    value = (T)Convert.ChangeType(valueStr, typeof(T));
                }
                catch(Exception)
                {
                    return defaultValue;
                }
            }

            return value;
        }

The usage changes as follows:

            int timeout = new ConfigKey("timeoutInMilliseconds").Get<int>(3000);
            bool enabled = new ConfigKey("enabled").Get<bool>(false);

That’s good enough for reading settings from App.config.

Dependency injection

In order to unit test our ConfigKey class, it’s best if we abstract out the dependency on App.config. In particular, we want to separate the part that reads the settings (reader) from the part that does the conversion and returns the value (provider).

For this, we need two interfaces. First, IConfigKeyReader is responsible to read the value of a setting from a source (e.g. App.config):

    public interface IConfigKeyReader
    {
        string Read(string key);
    }

Secondly, IConfigKeyProvider does all the rest: given a key, it returns a value (by internally using the IConfigKeyReader, which is not immediately evident from the interface):

    public interface IConfigKeyProvider
    {
        T Get<T>(string key, T defaultValue = default(T)) where T : IConvertible;
    }

The IConfigKeyReader implementation for reading from App.config is extremely simple:

    public class AppSettingReader : IConfigKeyReader
    {
        public string Read(string key)
        {
            return ConfigurationManager.AppSettings[key];
        }
    }

The IConfigKeyProvider for App.config settings is almost the same as the code we had in the previous section, with one important exception: it no longer depends directly on ConfigurationManager. Instead, it depends on the IConfigKeyReader which is injected in the constructor. This reader can be mocked in unit tests.

    public class ConfigKeyProvider: IConfigKeyProvider
    {
        private IConfigKeyReader reader;

        public ConfigKeyProvider(IConfigKeyReader reader)
        {
            this.reader = reader;
        }

        public T Get<T>(string key, T defaultValue = default(T)) where T : IConvertible
        {
            T value = defaultValue;

            string valueStr = reader.Read(key);

            if (valueStr != null)
            {
                try
                {
                    value = (T)Convert.ChangeType(valueStr, typeof(T));
                }
                catch (Exception)
                {
                    return defaultValue;
                }
            }

            return value;
        }
    }

You’ll also notice that we can now use a single instance of this AppSettingProvider to retrieve all our settings, rather than create a different ConfigKey for each setting. This approach is pretty handy if you’re using an IoC container to inject utility classes into your class constructors.

At this point we can throw away our old ConfigKey class, and instead use the new classes as follows:

            var reader = new AppSettingReader();
            var provider = new ConfigKeyProvider(reader);

            int timeout = provider.Get<int>("timeoutInMilliseconds", 3000);
            bool enabled = provider.Get<bool>("enabled", false);

Unit tests

Thanks to the separation between reader and provider, it is now easy to unit test our provider code while mocking our reader code. The reader will be source-specific and depends on external factors (e.g. files or databases) so it doesn’t make sense to unit test that. But we can unit test our provider, which handles the conversion and default values, and which will be reused whatever the reader (in fact notice the names used in the code above: AppSettingReader is specific to App.config AppSettings, but ConfigKeyProvider is used for any config key).

In the example unit test below, I’m using Moq to create a mock IConfigKeyReader, and thus test that the provider code works as expected:

        [TestMethod]
        public void Get_IntAvailableWithDefault_ValueReturned()
        {
            // arrange

            var key = "timeoutInMilliseconds";

            var reader = new Mock<IConfigKeyReader>();
            reader.Setup(r => r.Read(key)).Returns("5000");

            var provider = new ConfigKeyProvider(reader.Object);

            // act

            var expected = 5000;
            var actual = provider.Get<int>(key, 3000);

            // assert

            Assert.AreEqual(expected, actual);
        }

For the sake of brevity I won’t include the other unit tests here, but you can find them in the source code accompanying this article.

Database settings

The separation between reader and provider that we achieved in the previous section means that we can reuse the provider code (responsible for conversion and default values) regardless of the source of the settings. This means that anyone can write, for example, a DbSettingReader class which implements IConfigKeyReader and retrieves settings from a database. Its implementation would depend on the database structure so there won’t be any single standard implementation.

However, there is one improvement to our framework that we can make to facilitate reading settings from external sources such as databases. In particular, nowadays it is quite easy to query a database asynchronously without having to block the application. So it makes sense to add support for async methods in our interfaces so that anyone writing a DbSettingReader can then provide an asynchronous implementation.

IConfigKeyReader now becomes:

    public interface IConfigKeyReader
    {
        string Read(string key);

        Task<string> ReadAsync(string key);
    }

We now need to update our AppSettingReader implementation accordingly. Since reading AppSettings from App.config isn’t asynchronous, we can use Task.FromResult() to help satisfy the contract:

    public class AppSettingReader : IConfigKeyReader
    {
        public string Read(string key)
        {
            return ConfigurationManager.AppSettings[key];
        }

        public Task<string> ReadAsync(string key)
        {
            var value = this.Read(key);
            return Task.FromResult(value);
        }
    }

The provider code also needs to be updated to support asynchrony. First the interface:

    public interface IConfigKeyProvider
    {
        T Get<T>(string key, T defaultValue = default(T)) where T : IConvertible;

        Task<T> GetAsync<T>(string key, T defaultValue = default(T)) where T : IConvertible;
    }

The changes necessary to ConfigKeyProvider are a little more radical:

    public class ConfigKeyProvider : IConfigKeyProvider
    {
        private IConfigKeyReader reader;

        public ConfigKeyProvider(IConfigKeyReader reader)
        {
            this.reader = reader;
        }

        public T Get<T>(string key, T defaultValue = default(T)) where T : IConvertible
        {
            string valueStr = reader.Read(key);

            return this.ConvertValue<T>(valueStr, defaultValue);
        }

        public async Task<T> GetAsync<T>(string key, T defaultValue = default(T)) where T : IConvertible
        {
            string valueStr = await reader.ReadAsync(key).ConfigureAwait(false);

            return this.ConvertValue<T>(valueStr, defaultValue);
        }

        private T ConvertValue<T>(string valueStr, T defaultValue)
        {
            if (valueStr != null)
            {
                try
                {
                    return (T)Convert.ChangeType(valueStr, typeof(T));
                }
                catch (Exception)
                {
                    return defaultValue;
                }
            }
            else
                return defaultValue;
        }
    }

I opted to move the conversion code to a method shared by the async and non-async methods, and then call separate reader code in them. I intentionally avoided having Get() call GetAsync().Result as it can result in deadlocks.

Technically the best approach would have been to drop the synchronous Get() method altogether and force the use of the asynchronous version. However, I realise there are times when people actually want to call the synchronous version, such as in Console applications or in constructors (although there are workarounds for both – see Async Console Programs and “Can constructors be async?“).

Conclusion and Source Code

This article has presented a simple framework that can be used to read application settings without having to bloat actual program logic. It supports reading AppSettings from an App.config file out of the box, and can easily be extended to support other sources (e.g. databases). It makes it easy to provide default values, works nicely with dependency injection, and can also be used asynchronously.

Check out the source code at the Gigi Labs Bitbucket repository. Feel free to use this code as you like, and let me know if you think it can be improved.