当存储的数据类型发生变化时,如何升级Settings.settings?
我有一个应用程序,它在用户设置中存储对象集合,并通过 ClickOnce 进行部署。应用程序的下一版本对存储的对象类型进行了修改。例如,之前版本的类型是:
public class Person
{
public string Name { get; set; }
public int Age { get; set; }
}
而新版本的类型是:
public class Person
{
public string Name { get; set; }
public DateTime DateOfBirth { get; set; }
}
显然,ApplicationSettingsBase.Upgrade
不知道如何执行升级,因为 Age 需要使用 (age )=> DateTime.Now.AddYears(-age)
,因此只有 Name 属性会被升级,而 DateOfBirth 将只有 Default(DateTime) 的值。
因此,我想通过重写 ApplicationSettingsBase.Upgrade
提供一个升级例程,它将根据需要转换值。但我遇到了三个问题:
- 当尝试使用 ApplicationSettingsBase.GetPreviousVersion 访问先前版本的值时,返回的值将是当前版本的对象,该对象没有 Age 属性并且有一个空的 DateOfBirth 属性(因为它无法将 Age 反序列化为 DateOfBirth)。
- 我无法找到一种方法来找出我正在升级的应用程序版本。如果有从v1到v2的升级过程和从v2到v3的过程,如果用户从v1升级到v3,我需要按顺序运行两个升级过程,但如果用户从v2升级,我只需要运行第二次升级过程。
- 即使我知道应用程序的先前版本是什么,并且我可以访问其先前结构中的用户设置(例如通过获取原始 XML 节点),如果我想链接升级过程(如问题 2 中所述),我将在哪里存储中间值?如果从 v2 升级到 v3,升级过程将从 v2 读取旧值并将它们直接写入 v3 中的强类型设置包装类。但是,如果从 v1 升级,我会将 v1 到 v2 升级过程的结果放在哪里,因为应用程序只有 v3 的包装类?
我认为如果升级代码直接在 user.config 文件上执行转换,我可以避免所有这些问题,但我发现没有简单的方法来获取先前版本的 user.config 的位置,因为 LocalFileSettingsProvider。 GetPreviousConfigFileName(bool)
是一个私有方法。
是否有人有一个与 ClickOnce 兼容的解决方案,用于升级在应用程序版本之间更改类型的用户设置,最好是可以支持跳过版本的解决方案(例如,从 v1 升级到 v3,而不需要用户安装 v2)?
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)?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
我最终使用了一种更复杂的方法来进行升级,即从用户设置文件中读取原始 XML,然后运行一系列升级例程,将数据重构为新的下一个版本中应有的方式。另外,由于我在 ClickOnce 的
ApplicationDeployment.CurrentDeployment.IsFirstRun
属性中发现了一个错误(您可以查看 Microsoft Connect 反馈此处),我必须使用自己的 IsFirstRun 设置来了解何时执行升级。整个系统对我来说运行得很好(但由于一些非常顽固的障碍,它是用血和汗制成的)。忽略注释标记了我的应用程序特有的内容,而不是升级系统的一部分。使用了以下自定义扩展方法和帮助器类:
使用以下 Main 方法来允许系统工作:
我欢迎任何输入、批评、建议或改进。我希望这对某个地方的人有帮助。
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.IsFirstRun
property (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.
这可能并不是您正在寻找的答案,但听起来您试图将其作为升级来管理,而您不会继续支持旧版本,从而使问题变得过于复杂。
问题不仅仅是字段的数据类型正在发生变化,问题在于您完全改变了对象背后的业务逻辑,并且需要支持具有与旧业务逻辑和新业务逻辑相关的数据的对象。
为什么不继续拥有一个包含所有 3 个属性的 person 类呢?
当用户升级到新版本时,年龄仍然会被存储,因此当您访问 DateOfBirth 字段时,您只需检查 DateOfBirth 是否存在,如果不存在,您可以根据年龄计算并保存它,以便下次访问时它已经有出生日期,年龄字段可以忽略。
您可以将年龄字段标记为过时,这样您就记住将来不要使用它。
如果有必要,您可以向 person 类添加某种私有版本字段,以便在内部它知道如何根据它认为自己是什么版本来处理自身。
有时您确实必须拥有设计不完美的对象,因为您仍然需要支持旧版本的数据。
This may not really be the answer you are looking for but it sounds like you are overcomplicating the problem by trying to manage this as an upgrade where you aren't going to continue to support the old version.
The problem isn't simply that the data type of a field is changing, the problem is that you are totally changing the business logic behind the object and need to support objects that have data relating to both old and new business logic.
Why not just continue to have a person class which has all 3 properties on it.
When the user upgrades to the new version, the age is still stored, so when you access the DateOfBirth field you just check if a DateOfBirth exists, and if it doesn't you calculate it from the age and save it so when you next access it, it already has a date of birth and the age field can be ignored.
You could mark the age field as obsolete so you remember not to use it in future.
If necessary you could add some kind of private version field to the person class so internally it knows how to handle itself depending on what version it considers itself to be.
Sometimes you do have to have objects that aren't perfect in design because you still have to support data from old versions.
我知道这个问题已经得到解答,但我一直在玩弄这个问题,并想添加一种使用自定义类型处理类似(不相同)情况的方法:
如果 private _dob 和 public Age 都为 null 或 0,则您有另一个全部一起发出。在这种情况下,您始终可以默认将 DateofBirth 设置为 DateTime.Today。另外,如果您所掌握的只是一个人的年龄,您将如何将他们的出生日期精确到这一天?
I know this has already been answered but I have been toying with this and wanted to add a way I handled a similar (not the same) situation with Custom Types:
If both the private _dob and public Age is null or 0, you have another issue all together. You could always set DateofBirth to DateTime.Today by default in that case. Also, if all you have is an individual's age, how will you tell their DateOfBirth down to the day?