Gigi Labs

Please follow Gigi Labs for the latest articles.

Tuesday, April 15, 2014

C# AppSettings: Upgrading Settings between Assembly Versions

Hullo! :)

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:



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:


And when we run it again:



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:


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:


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:


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:


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:



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.


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.