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

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

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

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

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

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

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

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

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

Идеи?


person d7samurai    schedule 26.10.2010    source источник


Ответы (3)


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

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

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

person Wim Coenen    schedule 26.10.2010
comment
Я посмотрю. И да, я уверен, что хочу игнорировать их - я все равно не могу их использовать, и они испортили все, что я ДЕЙСТВИТЕЛЬНО хочу, так что это и ежу понятно, не так ли? :) - person d7samurai; 26.10.2010
comment
@ d7samurai: Некоторые люди попадают сюда из-за сообщения об исключении в Google. Я просто хотел подчеркнуть, что это не устраняет основную причину ошибки. Кроме того, введение тихого отказа (также известного как «ошибка возобновить следующее поведение») не совсем простая задача; это может значительно усложнить обнаружение и диагностику ошибок. Я рекомендую хотя бы дать понять пользователю, что случилось что-то нехорошее. - person Wim Coenen; 26.10.2010
comment
Конечно. Под игнорированием я подразумеваю двигаться дальше, не позволяя этому остановить весь процесс импорта. Нет пользователя, так как это служба Windows, но все, что делает этот (загрузчик), регистрируется, как обычные операции, так и исключения (включая сброс всех исключений в LoaderExceptions - если это применимо). - person d7samurai; 27.10.2010
comment
Кроме того, в этом случае цель импорта - обнаружить все доступные плагины (движка), чтобы впоследствии выбрать наиболее подходящий. И он всегда будет, поскольку базовый движок встроен в сам бутстраппер, на случай, если не будет найдено более качественных / новых / совместимых движков извне. - person d7samurai; 27.10.2010
comment
Отличный ответ. Решил мою проблему. Таким образом, я просто обнаружил поврежденную DLL и решил ее. - person Saeed Neamati; 06.06.2012

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

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

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

person BlackTigerX    schedule 02.12.2011
comment
Привет @BlackTigerX, Спасибо за подсказку по шаблону поиска. Решил мою проблему. - person Sau001; 08.04.2019

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

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>();
person Jan Remunda    schedule 17.10.2012
comment
Это не работает для ASP.NET MVC, если вы не используете отдельный домен, потому что Assembly.LoadFile загружает файл в другой домен. Чтобы решить эту проблему, вам просто нужно использовать Assembly.LoadFrom. - person Eben; 12.01.2018