【问题标题】:Deserialize types moved across assemblies反序列化跨程序集移动的类型
【发布时间】:2013-01-08 15:00:33
【问题描述】:

我有一堆类型已从一个程序集转移到另一个程序集。我正在尝试使用 SerializationBinder 将使用旧程序集序列化的数据反序列化到新程序集中的类型中。

编辑: 程序集的根命名空间与程序集名称相同。旧程序集已不存在。

sealed class TypeMapBinder : SerializationBinder
    {
        public override Type BindToType( string assemblyName, string typeName )
        {
            Type typeToDeserialize = null;

            if ( assemblyName.Contains( "old namespace" ) )
                typeToDeserialize = Type.GetType( typeName.Replace( "old namespace", "new namespace" ) );
            else
                typeToDeserialize = Type.GetType( String.Concat( typeName, ", ", assemblyName ) );

            return typeToDeserialize;
        }
    }

反序列化代码看起来像这样 -

using ( MemoryStream ms = new MemoryStream( byteArr ) )             {
                BinaryFormatter formatter = new BinaryFormatter( );
                formatter.Binder = new TypeMapBinder( );
                return formatter.Deserialize( ms );             
}

当我尝试反序列化时,我在尝试加载旧程序集时遇到错误。

无法加载文件或程序集“旧程序集”或其之一 依赖关系。系统找不到指定的文件。

【问题讨论】:

  • 您的 BindToType 覆盖是否被调用?它是否构成正确的类型名称?
  • 是的,没有问题,typeToDeserialize 永远不会为空
  • 在“旧命名空间”和 AssemblyName 之间寻找匹配是没有意义的。显然,这需要是“旧程序集名称”。您混淆了这段代码,以至于无法再诊断它可能有什么问题。
  • @HansPassant:可能他使用了程序集的命名方案,其中它们都以基本命名空间作为名称。
  • @HansPassant:正如 marceln 提到的程序集名称和根命名空间在我的情况下是相同的,应该在问题中提到。

标签: c# .net binaryformatter binary-serialization


【解决方案1】:

我想我遇到了同样的问题。

我的 SerializationBinder 的 BindToType 方法不发出任何类型,它引用旧程序集,但 BinaryFormatter 仍然尝试加载旧程序集:

System.IO.FileNotFoundException : Could not load file or assembly 'Old.Interfaces, Version=3.0.0.0, Culture=neutral, PublicKeyToken=null' or one of its dependencies. The system cannot find the file specified.
   at System.Reflection.RuntimeAssembly._nLoad(AssemblyName fileName, String codeBase, Evidence assemblySecurity, RuntimeAssembly locationHint, ref StackCrawlMark stackMark, IntPtr pPrivHostBinder, Boolean throwOnFileNotFound, Boolean forIntrospection, Boolean suppressSecurityChecks)
   at System.Reflection.RuntimeAssembly.InternalLoadAssemblyName(AssemblyName assemblyRef, Evidence assemblySecurity, RuntimeAssembly reqAssembly, ref StackCrawlMark stackMark, IntPtr pPrivHostBinder, Boolean throwOnFileNotFound, Boolean forIntrospection, Boolean suppressSecurityChecks)
   at System.Reflection.RuntimeAssembly.InternalLoad(String assemblyString, Evidence assemblySecurity, ref StackCrawlMark stackMark, IntPtr pPrivHostBinder, Boolean forIntrospection)
   at System.Reflection.RuntimeAssembly.InternalLoad(String assemblyString, Evidence assemblySecurity, ref StackCrawlMark stackMark, Boolean forIntrospection)
   at System.Reflection.Assembly.Load(String assemblyString)
   at System.UnitySerializationHolder.GetRealObject(StreamingContext context)
   at System.Runtime.Serialization.ObjectManager.ResolveObjectReference(ObjectHolder holder)
   at System.Runtime.Serialization.ObjectManager.DoFixups()
   at System.Runtime.Serialization.Formatters.Binary.ObjectReader.Deserialize(HeaderHandler handler, __BinaryParser serParser, Boolean fCheck, Boolean isCrossAppDomain, IMethodCallMessage methodCallMessage)
   at System.Runtime.Serialization.Formatters.Binary.BinaryFormatter.Deserialize(Stream serializationStream, HeaderHandler handler, Boolean fCheck, Boolean isCrossAppDomain, IMethodCallMessage methodCallMessage)
   at System.Runtime.Serialization.Formatters.Binary.BinaryFormatter.Deserialize(Stream serializationStream)
   [...]

如果我为AppDomain.CurrentDomain.AssemblyResolve 添加一个处理程序来加载New.Interfaces 而不是Old.Interfaces 它会引发另一个异常:

System.TypeLoadException : Could not load type 'Old.Interfaces.MyClass' from assembly 'New.Interfaces, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null'.
   at System.Reflection.RuntimeAssembly.GetType(RuntimeAssembly assembly, String name, Boolean throwOnError, Boolean ignoreCase, ObjectHandleOnStack type)
   at System.Reflection.RuntimeAssembly.GetType(String name, Boolean throwOnError, Boolean ignoreCase)
   at System.UnitySerializationHolder.GetRealObject(StreamingContext context)
   at System.Runtime.Serialization.ObjectManager.ResolveObjectReference(ObjectHolder holder)
   at System.Runtime.Serialization.ObjectManager.DoFixups()

但是,已经为 Old.Interfaces.MyClass 类型调用了 BindToType 方法,正如我所说,在 BindToType 中,我没有返回可能引用旧类的单一类型。

此外,如果我更改被反序列化的二进制数据,以便字符串Old 的出现被New 替换,最终加载对象图。我对这个解决方案不太满意。

【讨论】:

    【解决方案2】:

    我自己也遇到了这个问题,而且几乎无法修复。

    我将某些类型从一个程序集移动到另一个程序集,现在我在旧版本中序列化的文件无法反序列化。我的 SerializationBinder 成功解析了每种类型(包括最终导致异常的类型),但我仍然收到错误。它直到稍后才崩溃,在反序列化程序中由 DoFixup 例程启动的代码部分中。

    事实证明,我的一个序列化类型有一个 Type 类型的成员(我知道,这很令人困惑)。存储在此 Type 属性中的 Type 信息在反序列化时不会通过 SerializationBinder,而是在内部解析并失败。 AssemblyResolve 也不会修复它。

    除了手动解析文件之外,反序列化此文件的唯一方法是将旧版本与可以读取它的以前的程序集一起包装,然后将其保存为中性类型。

    【讨论】:

      【解决方案3】:

      我也遇到了同样的问题:类型被移动到另一个程序集,有自定义绑定器返回正确的类型,但生成了 TypeLoadException。

      实际上有帮助的是:https://stackoverflow.com/a/19490593/434298 我的意思是将[assembly:TypeForwardedTo(typeof(TheType))] 添加到最初具有该类型的程序集中...

      所以对于您的问题,我建议您尝试一下,如果需要,制作没有真正的源代码的程序集,而只是[程序集:]

      希望这能帮助另一个可怜的人:|

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2018-08-05
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多