【问题标题】:System.Runtime.Serialization.SerializationException: Unable to find assembly MyAssemblySystem.Runtime.Serialization.SerializationException:找不到程序集 MyAssembly
【发布时间】:2013-02-20 22:15:11
【问题描述】:

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

基本上,我的 .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);
 }

【问题讨论】:

  • 如果可以的话,如何显示代码崩溃的地方以及序列化代码的结构......
  • 使用 fuslogvw.exe 解决程序集解析问题。

标签: c# serialization


【解决方案1】:

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

【讨论】:

  • 酷。正在寻找解决方案并尝试了很多。没有工作,但这个。 :) 非常感谢
【解决方案2】:

嗯,我用了一个有效的技巧!

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();

【讨论】:

    【解决方案3】:

    我想在 Sean Ed-Mananswer 的基础上进行构建,这很好,但不适用于我的情况。

    如果您可以实例化该类但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,这很有效。

    【讨论】:

      【解决方案4】:

      我在您的序列化和反序列化代码中看到的是使用 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。

      【讨论】:

        【解决方案5】:

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

        在我的情况下,我在开发早期临时创建了一个我打算序列化的类的副本(在进行序列化的程序集中),但是在反序列化时忘记了它!所以反序列化器从来没有访问过序列化的同一个类!

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

        【讨论】:

          【解决方案6】:

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

          <configuration>  
             <runtime>  
                <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">  
                   <probing privatePath="bin;bin2\subbin;bin3"/>  
                </assemblyBinding>  
             </runtime>  
          </configuration> 
          

          【讨论】:

            【解决方案7】:

            我有一个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);
                        }
                    }
                }
            }
            

            【讨论】:

              【解决方案8】:

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

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

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

              AppDomain.CurrentDomain.AssemblyResolve += new ResolveEventHandler(MyTypeResolveEventHandler);
              
              private Assembly MyTypeResolveEventHandler(object sender, ResolveEventArgs args)
              {
                  return typeof(MyType).Assembly;
              }
              

              【讨论】:

                【解决方案9】:

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

                【讨论】:

                  猜你喜欢
                  • 2021-11-07
                  • 1970-01-01
                  • 2011-03-05
                  • 2011-04-09
                  • 2010-10-19
                  • 2011-09-23
                  • 2019-06-06
                  • 2011-03-20
                  相关资源
                  最近更新 更多