【问题标题】:C# deserialization of System.Type throws for a type from a loaded assemblySystem.Type 的 C# 反序列化从加载的程序集中抛出类型
【发布时间】:2011-12-19 17:01:38
【问题描述】:

我有一个运行良好的应用程序a.exe 并加载了一个程序集b.dll,如果重要的话,它是一个 Prism 模块。此 dll 是从不在路​​径中但在 a.exe 所在目录中的目录加载的。

程序集的加载由 Prism 完成,设置如下:

public class MyModuleCatalog : ComposablePartCatalog
{
  private readonly AggregateCatalog _catalog;

  public MyModuleCatalog()
  {
      //directory Modules is not in the path, but all
      //dependencies of b.dll are, so b.dll gets loaded fine
    var asmCat = new AssemblyCatalog( "Modules/b.dll" );
    _catalog.Catalogs.Add( asmCat );
  }

  public override IQueryable<ComposablePartDefinition> Parts
  {
    get { return _catalog.Parts; }
  }
}

class BootStrapper : MefBootstrapper
{
  ....
  protected override void ConfigureAggregateCatalog()
  {
    base.ConfigureAggregateCatalog();

    AggregateCatalog.Catalogs.Add( new AssemblyCatalog( Assembly.GetExecutingAssembly() ) );
    AggregateCatalog.Catalogs.Add( new MyModuleCatalog() );
  }
  ....
}

b.dll中有一个类ImInB

[Export]
public class ImInB
{
  public void DoIt()
  {
    try
    {
      var stream = new MemoryStream();
      //using System.Runtime.Serialization.Formatters.
      var formatter = new BinaryBinaryFormatter();

        //serialize our type
      formatter.Serialize( stream, this.GetType() );

        //get it back
      stream.Position = 0;
      var obj = formatter.Deserialize( stream ); //this throws??
    }
    catch( Exception e )
    {
    }
  }
}

这只是示例代码,是持久化框架的一部分,可将设置加载/保存到数据库。对象的类型总是被序列化并作为数据库的键。反序列化后,将检索类型作为对加载对象的双重检查。 该函数从a.exe调用:

container.GetExportedValue<ImInB>().DoIt();

反序列化类型时引发的异常(之前已成功序列化两行)是:

"Could not load file or assembly 'b.dll, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null'
or one of its dependencies. The system cannot find the file specified."

问题:

  • 这怎么可能?该函数从 dll 中被调用,但是却说找不到该 dll。
  • 我该如何解决这个问题?我如何告诉Deserialize 嘿,那个 dll 已经加载了,不要去找它

更新 我的第二个问题基本上由 Felix K 回答;下面的代码解决了这个问题:

public static class AssemblyResolverFix
{
  //Looks up the assembly in the set of currently loaded assemblies,
  //and returns it if the name matches. Else returns null.
  public static Assembly HandleAssemblyResolve( object sender, ResolveEventArgs args )
  {
    foreach( var ass in AppDomain.CurrentDomain.GetAssemblies() )
      if( ass.FullName == args.Name )
        return ass;
    return null;
  }
}

//in main
AppDomain.CurrentDomain.AssemblyResolve += AssemblyResolverFix.HandleAssemblyResolve;

这也证明了程序集被有效加载,包括它的所有依赖项,所以第一个问题仍然存在:为什么框架不能自己解决这个问题对我来说是个谜。此外,我无法在使用大致相同结构的第二个应用程序中重现它。

【问题讨论】:

  • 这两个文件的名称空间是什么。也许您可以更改它们以匹配并在项目级别与您的使用相匹配。编译器会知道如何引用它,否则听起来您可能需要为它不能或应该找到的方法使用完全限定的命名空间..
  • 这个 d.dll 是否也依赖于其他程序集?通过反射加载时,需要满足所有程序集静态引用。
  • @DJKRAZE 你的意思是什么文件?所有有问题的代码都在一个单一的类中,在一个单一的命名空间中。虽然 a.exe 与 b.dll 有不同的命名空间
  • @zenwalker 是的 b.dll 依赖于其他程序集,但它们都已加载(否则代码如何运行?)
  • 您可以在同一项目中命名您的程序集以匹配..只要方法名称不冲突,否则您必须完全限定它们..

标签: c# serialization prism system.type


【解决方案1】:

我不知道为什么在 dll 已经加载时会发生这种情况,但我认为这与序列化本身无关,对我来说它看起来像一个 .NET 错误。

这可能会对您有所帮助,或为您指明正确的方向:

AppDomain current = AppDomain.CurrentDomain;
current.AssemblyResolve += new ResolveEventHandler(HandleAssemblyResolve);

static Assembly HandleAssemblyResolve(object sender, ResolveEventArgs args)
{
    /* Load the assembly specified in 'args' here and return it, 
       if the assembly is already loaded you can return it here */
}

每次缺少 dll 时都会调用 resolve 方法,因此当您的 dll 丢失时也会发生这种情况。 dotNET 找不到,因为它在“Modules”文件夹中,所以您必须自己解析引用。

【讨论】:

  • +1 这实际上解决了它。在 HandleAssemblyResolve 中,我只是循环加载的程序集并返回 Assembly.FullName 与 ResolveEventArgs.Name 匹配的程序集。既然这么简单,不知道为什么框架不能自己做。
  • @stijn 这当然是个好问题。你应该填写一个错误报告,也许你会得到答案。
  • 查看我对 x0n 的回答的评论:这不是一个真正的错误,尽管它确实令人不安
【解决方案2】:

类型标识是一个善变的东西。几年前,无与伦比的 Suzanne Cook 的这两篇博文确实为我澄清了这一点:

LoadFile 与 LoadFrom:http://blogs.msdn.com/b/suzcook/archive/2003/09/19/loadfile-vs-loadfrom.aspx 选择绑定上下文:http://blogs.msdn.com/b/suzcook/archive/2003/05/29/57143.aspx

【讨论】:

  • +赏金,这帮助我弄清楚了为什么会发生这种情况,某种程度上,在反射器的帮助下。在类型的反序列化期间,调用普通 Load 但可能与最初在 AssemblyCatalog 中加载程序集的上下文不同(它也使用普通 Load)。在 Load 的缺点下提到“除非您订阅 AppDomain.AssemblyResolve 事件,否则其他上下文中的依赖项不可用”
  • @stijn 感谢您的赏金,我想给 Suzanne 一半,但我认为她不再在 CLR 团队工作了 :)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-08-21
  • 1970-01-01
  • 2012-05-05
  • 2012-01-01
相关资源
最近更新 更多