How do you keep user.config settings across different assembly versions in .net?

Viewed 38812

Basically the problem is that each time the assembly version changes (i.e. the user installs a new version of the application) all their settings are reset the the defaults (or more accurately a new user.config file is created in a folder with a different version number as the name)

How can I keep the same settings when upgrading versions, since using ini files or the registry seem to be discouraged?

When we used Clickonce it seemed to be able to handle this, so it seems like it should be able to be done, but I'm not sure how.

7 Answers

ApplicationSettingsBase has a method called Upgrade which migrates all settings from the previous version.

In order to run the merge whenever you publish a new version of your application you can define a boolean flag in your settings file that defaults to true. Name it UpgradeRequired or something similar.

Then, at application start you check to see if the flag is set and if it is, call the Upgrade method, set the flag to false and save your configuration.

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

Read more about the Upgrade method at MSDN. The GetPreviousVersion might also be worth a look if you need to do some custom merging.

The next short solution works for me when we need to upgrade only once per version. It does not required additional settings like UpgradeRequired:

if (!ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.PerUserRoamingAndLocal).HasFile)
    Settings.Default.Upgrade();

Here's a variation on the solutions presented here that encapsulates the upgrade logic into an abstract class that settings classes can derive from.

Some proposed solutions use a DefaultSettingsValue attribute to specify a value that indicates when previous settings were not loaded. My preference is to simply use a type whose default value indicates this. As a bonus, a DateTime? is helpful debugging information.

public abstract class UserSettingsBase : ApplicationSettingsBase
{
    public UserSettingsBase() : base()
    {
        // Accessing a property attempts to load the settings for this assembly version
        // If LastSaved has no value (default) an upgrade might be needed
        if (LastSaved == null)
        {
            Upgrade();
        }
    }

    [UserScopedSetting]
    public DateTime? LastSaved
    {
        get { return (DateTime?)this[nameof(LastSaved)]; }
        private set { this[nameof(LastSaved)] = value; }
    }

    public override void Save()
    {
        LastSaved = DateTime.Now;
        base.Save();
    }
}

Derive from UserSettingsBase:

public class MySettings : UserSettingsBase
{
    [UserScopedSetting]
    public string SomeSetting
    {
        get { return (string)this[nameof(SomeSetting)]; }
        set { this[nameof(SomeSetting)] = value; }
    }

    public MySettings() : base() { }
}

And use it:

// Existing settings are loaded and upgraded if needed
MySettings settings = new MySettings();
...
settings.SomeSetting = "SomeValue";
...
settings.Save();

If your changes to user.settings are done programmatically, how about maintaining a copy of (just) the modifications to user.settings in a separate file, e.g. user.customized.settings?

You probably still want to maintain and load the modified settings in user.settings as well. But this way when you install a newer version of your application with its newer version of user.settings you can ask the user if they want to continue to use their modified settings by copying them back into the new user.settings. You could import them wholesale, or get fancier and ask the user to confirm which settings they want to continue to use.

EDIT: I read too quickly over the "more accurately" part about assembly versions causing a new user.settings to be installed into a new version-specific directory. Thus, the idea above probably doesn't help you, but may provide some food for thought.

Related