BinaryFormatter反序列化给出了SerializationException

时间:2010-01-22 19:53:59

标签: .net exception serialization binaryformatter

我得到了:

  

System.Runtime.Serialization.SerializationException:无法找到   程序集'myNameSpace,Version = 1.0.0.0,Culture = neutral,   公钥=空

当尝试反序列化另一个程序中的某些数据而不是我用它序列化的程序时。

经过一些谷歌搜索后,我发现显然只能使用共享程序集来完成。

但是,我的数据库已满了这个序列化对象,我需要一个实用程序来解决它们。有没有办法覆盖这种行为,只是将它提供给完全相同的类并强制它反序列化?


我已经找到了这个片段,但我不明白应该放置/使用它的方式和位置。

   static constructor() {
        AppDomain.CurrentDomain.AssemblyResolve += new ResolveEventHandler(CurrentDomain_AssemblyResolve);
   }

    static Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEventArgs args) {
        Assembly ayResult = null;
        string sShortAssemblyName = args.Name.Split(',')[0];
         Assembly[] ayAssemblies = AppDomain.CurrentDomain.GetAssemblies();
         foreach (Assembly ayAssembly in ayAssemblies) {
            if (sShortAssemblyName == ayAssembly.FullName.Split(',')[0]) {
                 ayResult = ayAssembly;
                 break;
            }
         }
         return ayResult;
    }

6 个答案:

答案 0 :(得分:13)

如果您知道对象,则可以在不需要DLL的情况下解决此问题...

http://spazzarama.com/2009/06/25/binary-deserialize-unable-to-find-assembly/

http://msdn.microsoft.com/en-us/library/system.runtime.serialization.serializationbinder(VS.71).aspx

  

使用“System.Runtime.Serialization.SerializationBinder”类。通过   继承自此类,可以重定向所有请求   对于从二进制格式化程序到您选择的类型的类型。

这是一个示例,它允许在当前程序集中找到类型,而不管最初创建序列化流的程序集版本是什么:

sealed class AllowAllAssemblyVersionsDeserializationBinder : System.Runtime.Serialization.SerializationBinder
{
    public override Type BindToType(string assemblyName, string typeName)
    {     
        String currentAssembly = Assembly.GetExecutingAssembly().FullName;

        // In this case we are always using the current assembly
        assemblyName = currentAssembly;

        // Get the type using the typeName and assemblyName
        Type typeToDeserialize = Type.GetType(String.Format("{0}, {1}",
            typeName, assemblyName));

        return typeToDeserialize;
    }
}

public static MyRequestObject Deserialize(byte[] b)
{
    MyRequestObject mro = null;
    var formatter = new System.Runtime.Serialization.Formatters.Binary.BinaryFormatter();
    using (var ms = new System.IO.MemoryStream(b))
    {
       // To prevent errors serializing between version number differences (e.g. Version 1 serializes, and Version 2 deserializes)
       formatter.Binder = new AllowAllAssemblyVersionsDeserializationBinder();

       // Allow the exceptions to bubble up
       // System.ArgumentNullException
       // System.Runtime.Serialization.SerializationException
       // System.Security.SecurityException
       mro = (MyRequestObject)formatter.Deserialize(ms);
       ms.Close();
       return mro;
    }
}

答案 1 :(得分:6)

您需要提供对原始类型以某种方式的引用,以便实用程序知道如何反序列化它。

简单的方法就是添加最初定义类型的DLL作为对实用程序项目的引用。

您发布的代码允许您在反序列化程序确定无法找到类型时动态加载相同的DLL。这是一种更难的方法(但 很难),但在这两种情况下,您都需要一个定义类型的DLL ...所以最简单的方法就是通过添加引用来静态链接。

如果您的类型当前不在DLL中(例如,如果它们在EXE中),我建议您将EXE中的类拉入新的DLL,并从原始项目和util中引用该DLL项目

答案 2 :(得分:4)

我遇到了类似的问题,我得到了以下链接的帮助: BinaryFormatterDeserialize-not-finding-a-type

基本上,您需要做的是在反序列化之前订阅AssemblyResolve事件。然后在反序列化后取消订阅..

AppDomain.CurrentDomain.AssemblyResolve +=
                new ResolveEventHandler(CurrentDomain_AssemblyResolve);
// CODE TO DESERIALIZE HERE

AppDomain.CurrentDomain.AssemblyResolve -= new ResolveEventHandler(CurrentDomain_AssemblyResolve);

这是我用来解决大会的方法:

static Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEventArgs args)
{
    try
    {
        if(args.Name == "MY ASSEMBLY NAME"))
        {
            //Load my Assembly 
            Assembly assem = Assembly.LoadFrom("MY ASSEMBLY PATH");
            if(assem != null)
                return assem;
        }
    }
    catch { ;}

    return Assembly.GetExecutingAssembly();
}

答案 3 :(得分:4)

JTtheGeek's answer是正确的,自定义SerializationBinder是解决此问题的方法。但是,该答案中给出的示例代码对于我的用例来说还不够。我需要一些可能的东西:

  1. 处理不匹配的版本号名称空间。
  2. 查看所有程序集,而不仅仅是当前程序集。
  3. 仍然足够快,每秒可以拨打100次。
  4. 这就是我提出的:

    using System;
    using System.Collections.Generic;
    using System.Linq;
    
    namespace Company.Product.Common.Serialize
    {
        /// <summary>
        /// A Serialization Binder that allows inexact matches (version number or namespace).
        /// </summary>
        public sealed class AllowInexactMatchSerializationBinder : System.Runtime.Serialization.SerializationBinder
        {
            static private Dictionary<string, Type> typeBindings = new Dictionary<string, Type>();
    
            /// <summary>
            /// When overridden in a derived class, controls the binding of a serialized object to a type.
            /// </summary>
            /// <param name="assemblyName">Specifies the <see cref="T:System.Reflection.Assembly" /> name of the serialized object.</param>
            /// <param name="typeName">Specifies the <see cref="T:System.Type" /> name of the serialized object.</param>
            /// <returns>
            /// The type of the object the formatter creates a new instance of.
            /// </returns>
            public override Type BindToType(string assemblyName, string typeName)
            {
                Type type;
                var assemblyQualifiedTypeName = String.Format("{0}, {1}", typeName, assemblyName);
    
                // use cached result if it exists
                if (typeBindings.TryGetValue(assemblyQualifiedTypeName, out type))
                {
                    return type;
                }
    
                // try the fully qualified name
                try { type = Type.GetType(assemblyQualifiedTypeName); }
                catch { type = null; }
    
                if (type == null)
                {
                    // allow any assembly version
                    var assemblyNameWithoutVersion = assemblyName.Remove(assemblyName.IndexOf(','));
                    var assemblyQualifiedTypeNameWithoutVersion = String.Format("{0}, {1}", typeName, assemblyNameWithoutVersion);
                    try { type = Type.GetType(assemblyQualifiedTypeNameWithoutVersion); }
                    catch { type = null; }
                }
    
                if (type == null)
                {
                    // check all assemblies for type full name
                    try
                    {
                        type = AppDomain.CurrentDomain.GetAssemblies()
                            .SelectMany(a => a.ExportedTypes)
                            .Where(a => a.FullName == typeName)
                            .FirstOrDefault();
                    }
                    catch { type = null; }
                }
    
                if (type == null)
                {
                    // check all assemblies for type name
                    var name = typeName.Split('.').Last();
                    try
                    {
                        type = AppDomain.CurrentDomain.GetAssemblies()
                            .SelectMany(a => a.ExportedTypes)
                            .Where(a => a.Name == name)
                            .FirstOrDefault();
                    }
                    catch { type = null; }
                }
    
                typeBindings[assemblyQualifiedTypeName] = type;
                return type;
            }
        }
    }
    

答案 4 :(得分:1)

如果您无权访问序列化数据的原始程序集,则可以使用SerializationBinder或SerializationSurrogate。这两个接口允许您在反序列化时控制类型之间的转换方式。

答案 5 :(得分:0)

我按照JTtheGeek回答的解决方案,为了让它适合我,我必须在声明assemblyName = currentAssembly;之前添加以下内容:

typeName = "yourNamespace.yourClassName";

之后它运作得很好!