I have an application that stores a collection of objects in the user settings, and is deployed via ClickOnce. The next version of the applications has a modified type for the objects stored. For example, the previous version’s type was:
public class Person
{
public string Name { get; set; }
public int Age { get; set; }
}
And the new version’s type is:
public class Person
{
public string Name { get; set; }
public DateTime DateOfBirth { get; set; }
}
Obviously, ApplicationSettingsBase.Upgrade wouldn’t know how to perform an upgrade, since Age needs to be converted using (age) => DateTime.Now.AddYears(-age), so only the Name property would be upgraded, and DateOfBirth would just have the value of Default(DateTime).
So I’d like to provide an upgrade routine, by overriding ApplicationSettingsBase.Upgrade, that would convert the values as needed. But I’ve ran into three problems:
- When trying to access the previous version’s value using
ApplicationSettingsBase.GetPreviousVersion, the returned value would be an object of the current version, which doesn’t have the Age property and has an empty DateOfBirth property (since it can’t deserialize Age into DateOfBirth). - I couldn’t find a way to find out from which version of the application I’m upgrading. If there is an upgrade procedure from v1 to v2 and a procedure from v2 to v3, if a user is upgrading from v1 to v3, I need to run both upgrade procedures in order, but if the user is upgrading from v2, I only need to run the second upgrade procedure.
- Even if I knew what the previous version of the application is, and I could access the user settings in their former structure (say by just getting a raw XML node), if I wanted to chain upgrade procedures (as described in issue 2), where would I store the intermediate values? If upgrading from v2 to v3, the upgrade procedure would read the old values from v2 and write them directly to the strongly-typed settings wrapper class in v3. But if upgrading from v1, where would I put the results of the v1 to v2 upgrade procedure, since the application only has a wrapper class for v3?
I thought I could avoid all these issues if the upgrade code would perform the conversion directly on the user.config file, but I found no easy way to get the location of the user.config of the previous version, since LocalFileSettingsProvider.GetPreviousConfigFileName(bool) is a private method.
Does anyone have a ClickOnce-compatible solution for upgrading user settings that change type between application versions, preferably a solution that can support skipping versions (e.g. upgrading from v1 to v3 without requiring the user to in install v2)?
I ended up using a more complex way to do upgrades, by reading the raw XML from the user settings file, then run a series of upgrade routines that refactor the data to the way it’s supposed to be in the new next version. Also, due to a bug I found in ClickOnce’s
ApplicationDeployment.CurrentDeployment.IsFirstRunproperty (you can see the Microsoft Connect feedback here), I had to use my own IsFirstRun setting to know when to perform the upgrade. The whole system works very well for me (but it was made with blood and sweat due to a few very stubborn snags). Ignore comments mark what is specific to my application and is not part of the upgrade system.The following custom extention methods and helper classes were used:
The following Main method was used to allow the system to work:
I welcome any input, critique, suggestions or improvements. I hope this helps someone somewhere.