C#BinaryFormatter - 使用另一个命名空间中的对象反序列化

时间:2013-10-21 08:59:32

标签: c# .net serialization binary

最近,我们将部分代码移到了不同​​的项目库中。

不幸的是,似乎这些数据已经用BinaryFormatter序列化到数据库中(不要问我为什么,我不知道,我讨厌这个想法)。

现在我负责创建一个更新数据库的更新工具(当我们的软件根据版本检测到需要更新的数据库时,我们的软件会自动启动该工具):

  1. 创建新列
  2. 反序列化二进制列
  3. 将反序列化的列写入新列
  4. 删除旧的二进制列
  5. 我的问题是,当我尝试反序列化时,它会告诉我:

    Unable to find assembly 'MyOldAssemblyName, Version=2.0.0.0, Culture=neutral, PublicKeyToken=a5b9cb7043cc16da'.
    

    但是这个集会不再存在了。我把这个类放在我的“更新程序”项目中没有问题,但是我无法保持这个旧项目只包含这个文件。

    有没有办法指定BinaryFormatter它必须反序列化它用指定类接收的Stream?

    或者说程序集已重命名,或???

3 个答案:

答案 0 :(得分:4)

要告诉它类型已在程序集之间移动(但它保留旧名称和名称空间),有时可以使用(在程序集中)[assembly:TypeForwardedTo(typeof(TheType))]。这里的“有时”是因为你需要使用typeof,这意味着你需要从旧程序集到新程序集的引用,这并不总是可行 - 但经常是(特别是如果您要将类型从UI层移动到POCO / DTO层,因为UI通常引用POCO / DTO)。

但是,如果您重命名了更改名称空间的类型,则需要您编写自定义“活页夹”(see here)。

应该注意的是BinaryFormatter本质上是一个基于类型的序列化程序,在版本化或重构代码时,你总会遇到很多问题。如果类型不是“一次写入,那么永远不会改变它”,那么我 强烈 建议使用更灵活的东西 - 那就是合同基于而不是类型。基本上,除了BinaryFormatter(或NetDataContractSerializer)之外的任何事情:XmlSerializerDataContractSerializer,protobuf-net,Json.NET等中的任何一个都会很好并且< strong>不关心你重新定位或重命名了一个类型。

答案 1 :(得分:2)

事实上,我认为自己找到了解决方案。

我们可以给二元格式化程序SerializationBinder,这将允许我们手动解析我们在流中找到的类。

更多信息here

答案 2 :(得分:0)

我最近自己遇到了这个问题,并希望发布我是如何设法解决这个问题的。每个正常情况下序列化对象一切都是二进制格式化器反序列化时你可以使用一个Binder。使用下面的活页夹我简化了MSDN的解决方案,您需要做的就是将您使用的程序集的版本控制与类名一起交还。

    public object DeserializeObject()
    {
        BinaryFormatter binaryFormatter = new BinaryFormatter();
        binaryFormatter.Binder = new VersionDeserializer();

        using (MemoryStream memoryStream = new MemoryStream())
        {
            try
            {
                memoryStream.Write(_data, _ptr, count);
                memoryStream.Seek(0, SeekOrigin.Begin);
                return binaryFormatter.Deserialize(memoryStream);
            }
            catch (Exception e)
            {
                return null;
            }
        }
    }

    sealed class VersionDeserializer: SerializationBinder
    {
        public override Type BindToType(string assemblyName, string typeName)
        {
            Type deserializeType = null;
            String thisAssembly = Assembly.GetExecutingAssembly().FullName;
            deserializeType = Type.GetType(String.Format("{0}, {1}",
                typeName, thisAssembly));

            return deserializeType;
        }
    }