System.Type的C#反序列化会从已加载的程序集中抛出类型

sti*_*ijn 7 c# serialization prism system.type

我有一个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() );
  }
  ....
}
Run Code Online (Sandbox Code Playgroud)

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 )
    {
    }
  }
}
Run Code Online (Sandbox Code Playgroud)

这只是示例代码,是加载/保存设置到数据库的持久框架的一部分.对象的类型始终是序列化的,并且充当数据库的键.在反序列化时,将对类型作为对要加载的对象的双重检查进行检索.该函数从a.exe以下方式调用:

container.GetExportedValue<ImInB>().DoIt();
Run Code Online (Sandbox Code Playgroud)

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

"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."
Run Code Online (Sandbox Code Playgroud)

问题:

  • 这怎么可能呢?函数从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;
Run Code Online (Sandbox Code Playgroud)

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

Fel*_* K. 5

我不知道为什么在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 */
}
Run Code Online (Sandbox Code Playgroud)

每当缺少dll时,都会调用resolve方法,因此当你的dll丢失时也会发生这种情况.dotNET找不到它,因为它位于"Modules"文件夹中,因此您必须自己解析引用.