在进行二进制反序列化之前检查应用程序版本

时间:2013-06-25 15:43:50

标签: c# .net serialization visual-studio-2012

我有一个简单的应用程序,我将配置文件处理程序与其中的所有配置文件对象序列化到磁盘。它的效果很好......但它让我感到震惊,将来会出现问题。

下次更新软件时,配置文件可能会有更改,包含新字段和属性,或者某些类型可能已更改。当试图从早期版本反序列化时,这当然会破坏程序。

解决此问题的最简单,最直接的方法是什么?我想应该有办法在二进制文件中添加一些标题位,然后以某种方式检查它们。

这是我的序列化方法:

private void SaveProfilesToDisk()
{
    var serializer = new BinaryFormatter();

    string filename = Path.Combine(Environment.CurrentDirectory, @"MyApp\MyApp.profiles");
    using (FileStream fileStream = File.OpenWrite(filename))
    {
        serializer.Serialize(fileStream, _profileHandler);
    }
}

这就是我反序列化的方式:

 private ProfileHandler LoadProfilesFromDisk()
    {
        var serializer = new BinaryFormatter();

        string filename = Path.Combine(Environment.CurrentDirectory, @"MyApp\MyApp.profiles");
        using (FileStream fileStream = File.OpenRead(filename))
        {
            return (ProfileHandler) serializer.Deserialize(fileStream);
        }
    }

对我来说,只需告诉用户保存文件已过时,重命名/删除它并创建新文件即可。

1 个答案:

答案 0 :(得分:1)

编辑: 在重新阅读问题时,我意识到值得注意的是AssemblyResolve事件将收到Deserializer试图解决的程序集的版本。如果你想要的只是一个不兼容的版本消息,你可以陷入程序集名称作为程序集名称的位置,并且版本与当前版本不同,并相应地提示。

我同意@smoore最简单的方法是使用XmlSerialization,因为它可以反序列化发生非破坏性更改的以前版本的配置文件。 XmlSerializer将简单地忽略不存在的字段,并将愉快地反序列化那些字段。

如果由于某种原因确实需要二进制序列化,并且如果应用程序可以使用旧版本的对象,那么执行此操作的一种方法是不关心反序列化的版本并只是尝试匹配并反序列化可能的任何属性。对于非重大更改,例如添加新属性,您应该能够无问题地反序列化旧版本。

首先注册类型解析事件:

AppDomain.CurrentDomain.TypeResolve += new ResolveEventHandler(CurrentDomain_AssemblyResolve);
AppDomain.CurrentDomain.AssemblyResolve += new ResolveEventHandler(CurrentDomain_AssemblyResolve);

接下来,添加您的类型解析方法,并为您感兴趣的程序集返回这些程序集的当前版本。这是一个我做过这样的事情的例子:

Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEventArgs args) {
            Assembly result = null;
            foreach (Assembly assembly in AppDomain.CurrentDomain.GetAssemblies()) {
                AssemblyName assemblyName = assembly.GetName();
                if (args.Name.StartsWith(assemblyName.Name)) {
                    this.LogInfo("Assembly \"" + args.Name + "\" resolved to \"" + assembly.Location + "\".");
                    result = assembly;
                    break;
                }
            }
            if(result != null){
                return result;
            }else{
                this.LogError("Assembly resolution failure. An assembly named \"" + args.Name + "\" was not found.");
                return null;
            }
        }

如果我正确思考,这应该将类型解析为新版本,并允许您反序列化发生非破坏性更改的旧版本。对于破坏更改,最简单的方法是捕获SerializationException并相应地提示用户。