Assembly.GetTypes() - ReflectionTypeLoadException
Мы реализуем платформу плагинов для нашего приложения и загружаем сборки плагинов с помощью Assembly.Loadfrom. Затем мы используем GetTypes() и дополнительно изучаем типы с каждым файлом плагина для поддерживаемых интерфейсов.
Путь к плагинам предоставляется пользователем, и мы просматриваем каждый из файлов в папке, чтобы увидеть, поддерживает ли он (плагин) наш интерфейс плагина. Если это так, мы создаем экземпляр, если мы не перейдем к следующему файлу.
Мы создаем две версии программного обеспечения из одной базы кода (appA_1 и appA_2).
Загрузка плагинов хорошо работает, когда плагины загружаются приложением, которое было создано одновременно с файлом плагина. Однако, если мы создадим appA_2 и укажем на папку плагина appA_1, мы получим исключение, когда вызывается GetTypes().
Базовая версия нашего кода:
var pluginAssembly = Assembly.LoadFrom(FileName);
foreach (var pluginType in pluginAssembly.GetTypes())
{
Мы получаем исключение "ReflectionTypeLoadException".
Это связано с тем, что мы хотим, чтобы наше приложение могло загружать типы любого плагина, созданного кем-либо. Что-то нам не хватает?
EDIT:
После итерации через LoaderExceptions мы обнаружили, что существует один файл libPublic.dll, который генерирует исключение System.IO.FileNotFoundException. Странно, что этот файл находится в каталоге приложения, а плагин ссылается на файл проекта.
ИЗМЕНИТЬ 2:
В журнале исключений мы находим следующее
"Сравнение названия сборки привело к несоответствию: номер версии"
Ответы
Ответ 1
Несколько вещей:
-
Удостоверьтесь, что у вас нет дубликатов сборок в каталоге плагинов (т.е. сборок, которые вы уже загрузили в своем основном приложении из каталога вашего приложения.) В противном случае при загрузке вашего плагина он может загрузить дополнительную копию той же сборки. Это может привести к забавным исключениям, например:
Объект (типа "MyObject" ) не относится к типу "MyObject".
-
Если вы создаете исключение при создании экземпляра типа, вам может потребоваться обработать AppDomain.AssemblyResolve
:
private void App_Startup(object sender, StartupEventArgs e)
{
// Since we'll be dynamically loading assemblies at runtime,
// we need to add an appropriate resolution path
// Otherwise weird things like failing to instantiate TypeConverters will happen
AppDomain.CurrentDomain.AssemblyResolve += CurrentDomain_AssemblyResolve;
}
private Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEventArgs args)
{
var domain = (AppDomain) sender;
foreach (var assembly in domain.GetAssemblies())
{
if (assembly.FullName == args.Name)
{
return assembly;
}
}
return null;
}
Я понимаю, что немного странно говорить CLR, что для того, чтобы разрешить сборку, найдите сборку с именем, которое мы используем для разрешения, но я видел странные вещи без этого. Например, я мог бы создавать типы из сборки плагинов, но если бы я попытался использовать TypeDescriptor.GetConverter
, он не нашел бы TypeConverter
для класса, хотя мог бы видеть атрибут Converter
в классе.
Глядя на ваши изменения, это, вероятно, не то, что вызывает ваше текущее исключение, хотя вы можете столкнуться с этими проблемами позже, когда работаете со своими плагинами.
Ответ 2
Благодаря этому сообщению я смог решить ReflectionTypeLoadException
, который я получал в UITypeEditor
. Это сборка конструктора (интеллектуальный тег winforms, используемый во время разработки) библиотеки пользовательских классов, которые сканируют некоторые типы.
/// <summary>
/// Get the types defined in the RootComponent.
/// </summary>
private List<Type> getAssemblyTypes(IServiceProvider provider)
{
var types = new List<Type>();
try
{
IDesignerHost host = (IDesignerHost)provider.GetService(typeof(IDesignerHost));
ITypeResolutionService resolution = (ITypeResolutionService)provider.GetService(typeof(ITypeResolutionService));
AppDomain.CurrentDomain.AssemblyResolve += (sender, args) =>
{
foreach (var assembly in ((AppDomain)sender).GetAssemblies())
{
if (assembly.FullName == args.Name)
{
return assembly;
}
}
return null;
};
Type rootComponentType = resolution.GetType(host.RootComponentClassName, false);
types = rootComponentType.Assembly.GetTypes().ToList();
}
catch
{
}
return types;
}
Ответ 3
Вы получаете несоответствие версии сборки. Поскольку ваши плагины относятся к этому libPublic.dll
, вы должны тщательно его модифицировать и, в частности, не удалять его версию/сборку/etc. числа при каждом компиляции.