12

所以我找到了一堆关于这个主题的主题,但我认为我还没有找到一个适用的主题。

基本上我的 .exe 加载一个 .dll (MyAssembly) 文件,该文件进行序列化和加载。显然它序列化得很好。

但是,当我去反序列化 MyAssembly.dll 文件中的文件时,它会因本文标题中的错误而爆炸。

有人有想法么?我不明白它怎么找不到调用代码的程序集!

我的代码:

// deserialize


 using (var target = new System.IO.FileStream(Path, System.IO.FileMode.OpenOrCreate))
 {
     var bin = new System.Runtime.Serialization.Formatters.Binary.BinaryFormatter();
     var Obj = bin.Deserialize(target);
     if (Obj != null)
     {
         ObjectToStore = (ObjectTypeInMyAssembly)Obj;
     }
 }

// serialize
 using (var target = new System.IO.FileStream(Path, System.IO.FileMode.OpenOrCreate))
 {
     var bin = new System.Runtime.Serialization.Formatters.Binary.BinaryFormatter();
     bin.Serialize(target, ObjectToStore);
 }
4

9 回答 9

10

DLL 是否与 EXE 位于同一文件夹中?
我看到您序列化/反序列化位于 DLL 中的对象(“MyAssembly”)。反序列化时,格式化程序从序列化数据中确定类型的名称,并尝试在程序集中的主可执行文件夹,即-EXE 文件夹中找到该类型。
解决方案 - 将 DLL 移动到 EXE 文件夹。有一种方法可以使格式化程序在另一个程序集中搜索,捕获事件AppDomain.AssemblyResolve并返回您的 DLL。请参阅MSDN

于 2013-02-21T01:52:45.143 回答
6

好吧,我使用了一个有效的技巧!

sealed class CustomizedBinder : SerializationBinder
{
    public override Type BindToType(string assemblyName, string typeName)
    {
        Type returntype = null;
        string sharedAssemblyName = "SharedAssembly, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null";
        assemblyName = Assembly.GetExecutingAssembly().FullName;
        typeName = typeName.Replace(sharedAssemblyName, assemblyName);
        returntype =
                Type.GetType(String.Format("{0}, {1}",
                typeName, assemblyName));

        return returntype;
    }

    public override void BindToName(Type serializedType, out string assemblyName, out string typeName)
    {
        base.BindToName(serializedType, out assemblyName, out typeName);
        assemblyName = "SharedAssembly, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null";
    }
}

使用二进制格式化程序的活页夹,如下所示:

BinaryFormatter bf = new BinaryFormatter();
bf.Binder = new CustomizedBinder();
于 2014-05-29T17:51:02.160 回答
2

我想以Sean Ed-Man回答为基础,这很好,但在我的情况下不起作用。

如果您可以实例化该类但BinaryFormatter无法解决它,这可能对您有用。

在我的例子中,调用程序集(PluginAssembly在本例中)作为插件从可执行文件以 zip 文件的形式运行。出于某种原因,我可以NeededAssembly在实例化时直接解析类(来自),但BinaryFormatter无法解析它。NeededAssembly当然,作为对PluginAssembly项目的引用包含在内,这就是我可以实例化的原因。我不知道为什么不BinaryFormatter一样。

无论如何,这对我有用:

public class PluginAssembly
{
    // (class code here)

    private sealed class CustomizedBinder : SerializationBinder
    {
        public override Type BindToType(string assemblyName, string typeName)
        {
            Type returntype = null;
            if (typeName.StartsWith("NeededAssembly.RemoteClient.MessagePayload"))
            {
                returntype = typeof(MessagePayload);
            }
            else if (typeName.StartsWith("NeededAssembly.RemoteClient.ResultsPayload"))
            {
                returntype = typeof(ResultsPayload);
            }
            else if (typeName.Equals("System.Collections.Generic.List`1[[NeededAssembly.ShortResult, NeededAssembly, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null]]"))
            {
                returntype = typeof(List<ShortResult>);
            }
            else
            {
                returntype =
                        Type.GetType(String.Format("{0}, {1}",
                        typeName, assemblyName));
            }
            return returntype;
        }

        public override void BindToName(Type serializedType, out string assemblyName, out string typeName)
        {
            base.BindToName(serializedType, out assemblyName, out typeName);
            if (serializedType.ToString().Contains("NeededAssembly"))
            {
                assemblyName = typeof(MessagePayload).Assembly.FullName;
            }
        }
    }
}

当然,不要忘记使用它:

BinaryFormatter bf = new BinaryFormatter();
bf.Binder = new CustomizedBinder();

基本上,我只是typeof为所需的课程获得了一个,它有效。

于 2015-07-23T19:28:50.520 回答
0

我在您的序列化和反序列化代码中看到的是使用 System.IO.Path 类而不是文件的实际路径。我通常按​​如下方式执行:

    static void Main(string[] args)
    {
        string filepath = @"C:\FileLocation\";
        string filename = "datasaved.bin";
        MyClass something = new MyClass();

        // serialize
        using ( FileStream strm = File.OpenWrite(Path.Combine(filepath, filename)))
        {
            BinaryFormatter ser = new BinaryFormatter();
            ser.Serialize(strm, something);
        }

        // deserialize
        using (FileStream strm = File.OpenRead(Path.Combine(filepath, filename)))
        {
            BinaryFormatter ser = new BinaryFormatter();
            something = ser.Deserialize(strm) as MyClass;
        }
    }

请注意两件事:执行Deserialize as将处理对象为 null 或不是预期类型。我也不会滥用 var。

于 2013-02-20T23:58:38.997 回答
0

这个问题还有另一种解决方案,即在 App.config 中列出包含程序集的文件夹。您可以通过添加一个probing元素来做到这一点,如下所示:

<configuration>  
   <runtime>  
      <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">  
         <probing privatePath="bin;bin2\subbin;bin3"/>  
      </assemblyBinding>  
   </runtime>  
</configuration> 
于 2017-08-21T07:58:38.797 回答
0

我只是偶然发现了同样的问题,并发现我的错误是我的探测路径中存在该程序集的两个版本。如果您首先进行组装,然后修复构建后事件以将其移动到您真正需要的位置,通常会发生这种情况。

于 2020-09-04T14:25:05.713 回答
0

我会检查 Marc Gravell 的回答是否适用......简而言之“一个选择是将类型移动到其他项目都引用的库 dll - 然后它只定义一次,它会很高兴”

就我而言,我暂时创建了一个我打算在开发早期序列化的类的副本(在进行序列化的程序集中),但是在反序列化时忘记了它!所以反序列化器永远无法访问与序列化相同的类!

还值得检查包含正在序列化的类的项目的属性 - 检查其程序集名称中的拼写错误。您可能还想尝试扁平化命名空间层次结构,看看是否有帮助。

于 2017-08-14T11:03:32.553 回答
0

我遇到了这个例外的问题。我正在为第三方应用程序编写 WPF 插件。应用程序加载我的程序集,该程序集应该反序列化来自另一个 dll 的类型,例如 MyType,该程序集位于作为对我的插件的引用添加的私有程序集中,并且与插件 dll 位于同一目录中。

我觉得奇怪的是我可以在插件中实例化 MyType,但是在同一个类中反序列化时会抛出这个异常。

对我来说的解决方案是RoadBump建议的,非常简单,但我不明白为什么周围的代码可以找到程序集(如果它不能这个方法将不起作用)但是在同一代码中的反序列化调用,可以'吨。

AppDomain.CurrentDomain.AssemblyResolve += new ResolveEventHandler(MyTypeResolveEventHandler);

private Assembly MyTypeResolveEventHandler(object sender, ResolveEventArgs args)
{
    return typeof(MyType).Assembly;
}
于 2019-04-05T13:52:51.577 回答
0

我有一个OP描述的SerializationException的具体案例,并找到了一个相对简单的解决方案。就我而言,我正在使用 BinaryFormatter 技术实现 DeepClone 扩展方法。我的应用程序还使用了我以编程方式加载的插件和程序集。为类包含在插件中的对象调用 BinaryFormatter.Deserialize 方法时,我遇到了 SerializationException。

我觉得这很奇怪,因为根据定义,包含程序集被加载到我的 AppDomain 中。(如果不是,我无法在对象上调用扩展方法!)显然,BinaryFormatter 不会检查 AppDomain 来解析程序集。

此处为解决此问题而建议的许多解决方案似乎都涉及将程序集或类的名称硬编码到自定义 SerializationBinder 中。这没有用,因为我希望我的 DeepClone 方法尽可能通用。我对这个问题的回答(如下所示)将 AssemblyResolve 事件挂接到当前 AppDomain 中,然后在 AppDomain(使用 LINQ)中搜索请求的程序集。不需要硬编码的程序集或类名。

这是代码:

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.Serialization.Formatters.Binary;
using System.Runtime.Serialization;

namespace StackOverflowDemo
{
    public static class Extend
    {
        /// <summary>
        /// Static initializer is used to register the Resolve Event Handler
        /// </summary>
        static Extend()
        {
            AppDomain.CurrentDomain.AssemblyResolve += ResolveEventHandler;
        }

        /// <summary>
        /// This resolver will find any Assembly that is already loaded into
        /// the current AppDomain and return it.
        /// <para/>
        /// You would think this would not be necessary, but the 
        /// BinaryFormatter.Deserialize method apparently can not 
        /// find an assembly that has been loaded programmatically
        /// as a plug-in, and whose DLL does not reside in the same
        /// folder as the executable file.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="args"></param>
        /// <returns></returns>
        private static Assembly ResolveEventHandler( object sender, ResolveEventArgs args )
        {
            var assemblies = AppDomain.CurrentDomain.GetAssemblies();
            Assembly result = (from a in assemblies where args.Name.Equals(a.FullName) select a).FirstOrDefault();
            return result;
        }


        /// <summary>
        /// Slow, and requires that the source be marked as [Serializable], but 
        /// clones *everything* and is not complicated.
        /// <para/>
        /// Note that by itself, this method will fail if an attempt is made to
        /// create a deep copy of an object whose class is contained in an assembly
        /// that was loaded programmatically (i.e., loaded as a plug-in).
        /// <para/>
        /// See https://stackoverflow.com/a/1213649
        /// <para/>
        /// and https://stackoverflow.com/a/23017515
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="src"></param>
        /// <returns></returns>
        public static T DeepClone<T>( this T src )
        {
            if (!typeof(T).IsSerializable)
            {
                throw new ArgumentException(string.Format("[Extend.DeepClone] Type '{0}' is not Serializable", typeof(T).Name));
            }

            if (Object.ReferenceEquals(src, null))
            {
                return default(T);
            }

            using (Stream stream = new MemoryStream())
            {
                IFormatter formatter = new BinaryFormatter();
                formatter.Serialize(stream, src);
                stream.Seek(0, SeekOrigin.Begin);
                return (T)formatter.Deserialize(stream);
            }
        }
    }
}
于 2019-02-14T15:36:34.847 回答