32

Background
I have a simple WinForms application written in C#. I debated deployment solutions for a while but ultimately decided to abandon ClickOnce as a few of the constraints were crucially restrictive.

Instead, I've adapted a simple solution of versioning the application via the assembly/file versions (I keep them in sync) in the application's properties. I'm deploying via a Setup Project (*.msi). I store the latest assembly version number in an XML file online, as well as the latest installer file. At run-time, I simply check the Application.ProductVersion against the latest version online and open an update dialog if an update is available.

Problem
This has worked pretty well thus far, but I've recently noticed a major problem with this approach. When the assembly version of the application is updated, a new version of the user's settings file (user.config) is created in AppData/Company/Product/Version/blahblahblah. This obviously forces the user to reset everything in the new version.

Suggested Solutions
I'm not sure how to proceed. The application only has 1 release thus far and the current user base is basically whoever I can beg to test it, so switching up strategies is no big deal. I've considered:

1.) Write my own settings system and thus have complete control over where/how the settings file is stored and used.
2.) Re-think my versioning/update strategy so that the update is not based on the assembly version. I'm not sure how I would do this, but my testing seemed to reveal that even building and installing a new version with the same assembly version would still break user.config.

I guess what I'm truly asking if there is any way to preserve the default settings system since it's so easy to use while also adapting it to my deployment strategy.

Tyler Daniels
  • 613
  • 6
  • 19

3 Answers3

64

Use the built in Settings classes, you just need to upgrade the settings anytime you change the application version. Here's how to do it: In the Settings.settings file, create a new setting UpdateSettings type=bool Scope=User Value=True

Include the following code before you use any Settings (it can run every time the app runs, as this makes running in debugger easier too)

// Copy user settings from previous application version if necessary
if (MyApp.Properties.Settings.Default.UpdateSettings)
{
    MyApp.Properties.Settings.Default.Upgrade();
    MyApp.Properties.Settings.Default.UpdateSettings = false;
    MyApp.Properties.Settings.Default.Save();
}

When your new application version is run UpdateSettings will have a default value of True and none of your old settings will be used. If UpdateSettings is true we upgrade the settings from the old settings and save then under the new app version.

Grant
  • 1,608
  • 16
  • 14
  • 1
    Wow, I was completely unaware of the UpdateSettings() method. Thanks for pointing that out! This solution has worked perfectly in my test environment. – Tyler Daniels May 29 '14 at 03:02
  • 4
    @SeeSharpCode You mean the Upgrade() method. UpdateSettings is a setting you have to define yourself. – Kyle Delaney Jul 19 '17 at 13:44
  • I'm not a WinForms developer, so please excuse me if this is a trivial question... What if `UpdateSettings() == false`? Would you suggest just using a local config file of some kind instead? – Will Strohl May 05 '19 at 00:24
  • Unfortunately it doesn't work in VS2022 .NET 7. Previous version settings are not restored. – arteny Feb 21 '23 at 19:27
5

Here's how I solved it.

In the GUI application it is very easy to restore the settings by executing

Properties.Settings.Default.Upgrade();
Properties.Settings.Default.Reload();
Properties.Settings.Default.Save();

However, I've always had the problem that all other libraries lost their settings when a new version has been installed. With the following implementation the software runs through all assemblies of the AppDomain and restores the settings of the respective library:

foreach(var _Assembly in AppDomain.CurrentDomain.GetAssemblies())
{
    foreach(var _Type in _Assembly.GetTypes())
    {
        if(_Type.Name == "Settings" && typeof(SettingsBase).IsAssignableFrom(_Type))
        {
            var settings = (ApplicationSettingsBase)_Type.GetProperty("Default").GetValue(null, null);
            if(settings != null)
            {
                settings.Upgrade();
                settings.Reload();
                settings.Save();
            }
        }
    }
}

I've implemented the code in the App.xaml.cs of the GUI project and it will always be executed when the setting "NewVersionInstalled" was set to true by a new version.

Hope this helps!

dontbyteme
  • 1,221
  • 1
  • 11
  • 23
3

I prefer this alternative which does not need the additional setting Settings.Default.UpdateSettings

string configPath = ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.PerUserRoamingAndLocal).FilePath;
if (!File.Exists(configPath))
{
    //Existing user config does not exist, so load settings from previous assembly
    Settings.Default.Upgrade();
    Settings.Default.Reload();
    Settings.Default.Save();
}
rayzinnz
  • 1,639
  • 1
  • 17
  • 17