Подтвердить что ты не робот

MEF: "Невозможно загрузить один или несколько запрошенных типов. Получить дополнительную информацию об LoaderExceptions"

Сценарий: я использую Managed Extensibility Framework для загрузки плагинов (экспорта) во время выполнения на основе контракта интерфейса, определенного в отдельной dll. В моем решении Visual Studio у меня есть 3 разных проекта: приложение-хост, библиотека классов (определение интерфейса - "IPlugin" ) и другая библиотека классов, реализующая интерфейс (экспорт - "MyPlugin.dll" ).

Хост ищет экспорт в своем корневом каталоге, поэтому во время тестирования я создаю все решение и скопирую файл Plugin.dll из папки bin/release библиотеки Plugin в каталог отладки хоста, чтобы найти его Directory Directory Catalog и сможете добавить его в CompositionContainer. Plugin.dll автоматически не копируется после каждого восстановления, поэтому я делаю это вручную каждый раз, когда я вносил изменения в контракт/реализацию.

Тем не менее, пару раз я запускал приложение-хост, не скопировав (обновленный) файл Plugin.dll, и он создал исключение во время композиции:

Unable to load one or more of the requested types. Retrieve the LoaderExceptions for more information

Это, конечно, связано с тем, что файл Plugin.dll, который он пытается импортировать, реализует другую версию IPlugin, где сигнатуры свойств/методов не совпадают. Хотя это легко избежать в контролируемой и контролируемой среде, просто избегая (duh) устаревших реализаций IPlugin в папке плагина, я не могу полагаться на такие предположения в рабочей среде, где могут встречаться устаревшие плагины.

Проблема заключается в том, что это исключение эффективно помещает все действие Compose и импортируется экспорт no. Я бы предпочел, чтобы несовместимые реализации IPlugin просто игнорировались, так что другие экспортируемые в каталоге (каталоги), реализующие правильную версию IPlugin, все еще импортируются.

Есть ли способ сделать это? Я думаю об одном из нескольких возможных вариантов:

  • В CompositionContainer ( "игнорировать сбой импорта" ) есть флаг, который должен быть установлен до или при вызове Compose
  • Существует аналогичный флаг для атрибута <ImportMany()>
  • Существует способ "подключиться" к процессу итерации, лежащему в основе Compose(), и иметь возможность обрабатывать каждый (неудачный) импорт отдельно
  • Использование сильной подписи имени для того, чтобы как-то только искать импорт, реализующий версию current IPlugin

Идеи?

4b9b3361

Ответ 1

Я также столкнулся с аналогичной проблемой.

Если вы уверены, что хотите игнорировать такие "плохие" сборки, тогда решение должно вызвать AssemblyCatalog.Parts.ToArray() сразу после создания каждого каталога сборки. Это вызовет ReflectionTypeLoadException, о котором вы упоминаете. Затем у вас есть шанс поймать исключение и проигнорировать плохую сборку.

Когда вы создали объекты AssemblyCatalog для всех "хороших" сборок, вы можете объединить их в AggregateCatalog и передать это конструктору CompositionContainer.

Ответ 2

Эта проблема может быть вызвана несколькими факторами (любыми исключениями для загруженных сборок), например, как говорится в исключении, посмотрите на ExceptionLoader, чтобы (надеюсь) получить некоторую идею

Другая проблема/решение, которое я нашел, - это использование DirectoryCatalog, если вы не укажете второй параметр "searchPattern", MEF загрузит ВСЕ DLL в этой папке (включая стороннюю), и начните искать типы экспорта, что также может вызвать эту проблему, решение должно иметь имя соглашения на всех сборках, которые экспортируют типы, и указать это в конструкторе DirectoryCatalog, я использую * _Plugin.dll, таким образом, MEF будет загружать только сборки, содержащие экспортируемые типы

В моем случае MEF загружает DLL NHibernate и бросает некоторую ошибку версии сборки на LoaderException (эта ошибка может произойти с любой из DLL в каталоге), этот подход разрешил проблему

Ответ 3

Вот пример вышеупомянутых методов:

var di = new DirectoryInfo(Server.MapPath("../../bin/"));

        if (!di.Exists) throw new Exception("Folder not exists: " + di.FullName);

        var dlls = di.GetFileSystemInfos("*.dll");
        AggregateCatalog agc = new AggregateCatalog(); 

        foreach (var fi in dlls)
        {
            try
            {
                var ac = new AssemblyCatalog(Assembly.LoadFile(fi.FullName));
                var parts = ac.Parts.ToArray(); // throws ReflectionTypeLoadException 
                agc.Catalogs.Add(ac);
            }
            catch (ReflectionTypeLoadException ex)
            {
                Elmah.ErrorSignal.FromCurrentContext().Raise(ex);
            }
        }

        CompositionContainer cc = new CompositionContainer(agc);

        _providers = cc.GetExports<IDataExchangeProvider>();