C # десериализация System.Type бросает для типа из загруженной сборки - PullRequest
7 голосов
/ 19 декабря 2011

У меня есть приложение 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 уже загружена, не ищите ее

ОБНОВЛЕНИЕ мой второй вопрос в основном ответилФеликс К;следующий код решает проблему:

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;

Это также доказывает, что сборка эффективно загружена, включая все ее зависимости, поэтому остается первый вопрос: для меня загадка, почему фреймворк не может понять этосамо собой.Более того, я не могу воспроизвести его во втором приложении, которое использует примерно ту же структуру.

Ответы [ 2 ]

5 голосов
/ 30 декабря 2011

Я не знаю, почему это происходит, когда 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 отсутствует, вызывается метод разрешения, поэтому это также должно происходить, когда ваша DLL отсутствует. dotNET не может его найти, потому что он находится в папке «Модули», поэтому вам нужно разрешить ссылку самостоятельно.

1 голос
/ 31 декабря 2011

Идентичность типа - переменчивая вещь.Эти два сообщения в блоге от неподражаемой Сюзанны Кук действительно прояснили это для меня несколько лет назад:

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

...