13

我得到一个:

System.Runtime.Serialization.SerializationException:找不到程序集'myNameSpace,版本 = 1.0.0.0,文化 = 中性,PublicKeyToken = null

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

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

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


我已经找到了这个片段,但我不明白我应该如何以及在哪里放置/使用它。

   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;
    }
4

6 回答 6

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;
    }
}
于 2010-12-02T22:56:01.877 回答
6

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

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

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

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

于 2010-01-22T20:02:13.553 回答
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();
}
于 2011-12-02T15:44:23.397 回答
4

JTtheGeek 的回答是正确的,自定义SerializationBinder是处理此问题的方法。但是,该答案中给出的示例代码不足以满足我的用例。我需要一些可以:

  1. 处理不匹配的版本号命名空间。
  2. 查看所有程序集,而不仅仅是当前程序集。
  3. 仍然足够快以每秒调用 100 次。

这就是我想出的:

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;
        }
    }
}
于 2014-08-20T19:01:27.697 回答
1

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

于 2011-11-04T20:21:08.370 回答
0

我遵循了 JTtheGeek 回答的解决方案,为了让它对我有用,我必须在声明之前添加以下内容assemblyName = currentAssembly;

typeName = "yourNamespace.yourClassName";

之后效果很好!

于 2013-11-27T11:12:49.673 回答