C# 二进制格式化程序 - 使用另一个命名空间中的对象进行反序列化

本文关键字:对象 反序列化 命名空间 另一个 格式化 二进制 程序 | 更新日期: 2023-09-27 18:36:08

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

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

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

  1. 创建新列
  2. 反序列化二进制列
  3. 将反序列化的列写入新列
  4. 删除旧的二进制列

我的问题是,当我尝试反序列化时,它告诉我:

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

但是这个程序集不再存在。我将此类放在我的"更新程序"项目中没有问题,但是我无法保留这个旧项目以仅包含此文件。

有没有办法向二进制格式化程序指定它必须反序列化它使用指定类接收的流?

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

C# 二进制格式化程序 - 使用另一个命名空间中的对象进行反序列化

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

但是,如果您重命名了更改的命名空间的类型,则需要编写自定义"绑定程序"(请参阅此处)。

应该注意的是,BinaryFormatter本质上是基于类型的序列化程序,在对代码进行版本控制或重构时,您总是会遇到很多问题。如果类型不是"写一次,然后永远不要改变它",那么我强烈建议使用更灵活的东西 - 有些是基于合约而不是基于类型的。基本上,除了BinaryFormatter(或NetDataContractSerializer)之外的任何东西:XmlSerializerDataContractSerializer,protobuf-net,Json.NET 等中的任何一个都可以,并且不会关心您重新定位或重命名类型。

事实上,

我想我找到了自己的解决方案。

我们可以对二进制格式化器进行SerializationBinder,这将使我们能够手动解析在流中找到的类。

更多信息请点击此处

我最近自己遇到了这个问题,想发布我如何解决这个问题。按正常序列化对象 所有内容的关键是当二进制格式化程序反序列化时,您可以使用 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;
        }
    }