MEF 未检测到插件依赖项
MEF not detecting plugin dependencies
我在使用 MEF 和使用插件文件夹时遇到问题。
我有一个通过 MEF 支持插件的主应用程序。主应用程序不引用包含用于多线程的 .NET 任务类型的程序集,但一个或多个插件引用。
插件位于 Plugins 文件夹中,我使用的是 DirectoryCatalog。
我一直收到 ReflectionTypeLoadException
被 MEF 在
上抛出
Unable to load one or more of the requested types. Retrieve the
LoaderExceptions property for more information.
LoaderExceptions 属性 包含 FileNotFoundException
"Could not load file or assembly 'System.Threading.Tasks,
Version=1.5.11.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a' or
one of its dependencies. The system cannot find the file
specified.":"System.Threading.Tasks, Version=1.5.11.0,
Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a"
插件正在通过 Microsoft NuGet 包引用引用 System.Threading.Tasks
。
这是我的辅助方法:
public static void Compose(IEnumerable<string> searchFolders, params object[] parts)
{
// setup composition container
var catalog = new AggregateCatalog();
// check if folders were specified
if (searchFolders != null)
{
// add search folders
foreach (var folder in searchFolders.Where(System.IO.Directory.Exists))
{
catalog.Catalogs.Add(new DirectoryCatalog(folder, "*.dll"));
}
}
catalog.Catalogs.Add(new AssemblyCatalog(typeof(Program).Assembly));
// compose and create plug ins
var composer = new CompositionContainer(catalog);
composer.ComposeParts(parts);
}
public class MEFComposer
{
[ImportMany(typeof(IRepository))]
public List<IRepository> Repositories;
[ImportMany(typeof(ILogging))]
public List<ILogging> LoggingRepositories;
[ImportMany(typeof(IPlugin))]
public List<IPlugin> Plugins;
}
这是我用来调用 MEF 和加载插件的代码。
public void Compose()
{
// try to connect with MEF types
try
{
var parts = new MEFComposer();
MEFHelpers.Compose(new[] { Path.Combine(Application.StartupPath, "Plugins") }, parts);
RepositoryFactory.Instance.Repository = parts.Repositories.FirstOrDefault();
Logging.Repositories.AddRange(parts.LoggingRepositories);
foreach (var plugin in parts.Plugins)
{
this.applicationApi.Plugins.Add(plugin);
plugin.Connect(this.applicationApi);
}
}
catch
{
// ERR: handle error
}
}
即使 Microsoft.Threading.Tasks.dll
和相关程序集文件存在于 Plugins 文件夹中,但主应用程序 bin 文件夹中没有,为什么 MEF 无法加载插件?有什么方法可以让 MEF 在 Plugins 文件夹中搜索程序集依赖项?
拥有插件模型意味着我无法预料插件可能引用了哪些程序集,因此我无法将它们包含在应用程序的主 bin 文件夹中,这就是为什么我希望所有相关的插件和插件依赖项都在插件文件夹。
您在支持第 3 方插件时遇到了 运行 一个基本问题。您的问题是,当您加载插件时,运行time 将在需要时仅在您的 AppDomain 已知的指定文件夹中搜索其引用。那就是 WorkingDirectory of that process, then path etc。
本质上,您正在加载一个需要 System.Threading.Tasks
的插件。该 DLL 位于您的 /Plugin
文件夹中。当 .net 加载您的插件时,它将搜索该程序集但无法找到它,因为它位于 /Plugin
文件夹中并且失败了。
有几个解决方案。
- 不要使用插件文件夹
这将是最简单的解决方案,当所有程序集(包括第 3 方库的引用)都位于您的 WorkingDirectory 中时,.net 将毫不费力地找到该插件的所有引用。
- 将插件文件夹添加到您的探测路径
这将扩展 .net 搜索第 3 方参考的路径:
https://docs.microsoft.com/en-us/dotnet/framework/deployment/how-the-runtime-locates-assemblies#locating-the-assembly-through-probing
- 为每个插件使用 AppDomains。
AppDomain 是我的选择,因为它不仅允许您在其 "own" 容器中加载程序集,而且还可以仅为插件模拟工作目录。例如,如果其中一个插件使用与您的应用程序相同的框架但版本不同,这会派上用场。
- 自行加载依赖
这就是解决这个问题的 "straight forward" 方法。您可以将每个程序集加载为 ReflectionOnly,确定所有依赖项,然后加载它们。这几乎可以保证工作。
4.1。 AssemblyResolve 事件
这只是 "redirect" .net 从 PluginFolder 加载程序集的另一种方式
https://docs.microsoft.com/en-us/dotnet/api/system.appdomain.assemblyresolve?view=netframework-4.8
编辑:
AssemblyCatalog 也有一定的问题,它使用 Assembly.Load
而不是 Assembly.LoadFrom
来加载给定的程序集。这是您的问题的重要组成部分,因为 LoadFrom
会探测程序集的来源路径以获取其依赖项,而 Load
则不会。
https://github.com/JPVenson/MSEF/blob/master/JPB.Shell/JPB.Shell.MEF/Model/StrongNameCatalog.cs
您可以使用像这样使用 LoadFrom 的目录。
免责声明:我是该项目的创建者。
我在使用 MEF 和使用插件文件夹时遇到问题。
我有一个通过 MEF 支持插件的主应用程序。主应用程序不引用包含用于多线程的 .NET 任务类型的程序集,但一个或多个插件引用。
插件位于 Plugins 文件夹中,我使用的是 DirectoryCatalog。
我一直收到 ReflectionTypeLoadException
被 MEF 在
Unable to load one or more of the requested types. Retrieve the LoaderExceptions property for more information.
LoaderExceptions 属性 包含 FileNotFoundException
"Could not load file or assembly 'System.Threading.Tasks, Version=1.5.11.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a' or one of its dependencies. The system cannot find the file specified.":"System.Threading.Tasks, Version=1.5.11.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a"
插件正在通过 Microsoft NuGet 包引用引用 System.Threading.Tasks
。
这是我的辅助方法:
public static void Compose(IEnumerable<string> searchFolders, params object[] parts)
{
// setup composition container
var catalog = new AggregateCatalog();
// check if folders were specified
if (searchFolders != null)
{
// add search folders
foreach (var folder in searchFolders.Where(System.IO.Directory.Exists))
{
catalog.Catalogs.Add(new DirectoryCatalog(folder, "*.dll"));
}
}
catalog.Catalogs.Add(new AssemblyCatalog(typeof(Program).Assembly));
// compose and create plug ins
var composer = new CompositionContainer(catalog);
composer.ComposeParts(parts);
}
public class MEFComposer
{
[ImportMany(typeof(IRepository))]
public List<IRepository> Repositories;
[ImportMany(typeof(ILogging))]
public List<ILogging> LoggingRepositories;
[ImportMany(typeof(IPlugin))]
public List<IPlugin> Plugins;
}
这是我用来调用 MEF 和加载插件的代码。
public void Compose()
{
// try to connect with MEF types
try
{
var parts = new MEFComposer();
MEFHelpers.Compose(new[] { Path.Combine(Application.StartupPath, "Plugins") }, parts);
RepositoryFactory.Instance.Repository = parts.Repositories.FirstOrDefault();
Logging.Repositories.AddRange(parts.LoggingRepositories);
foreach (var plugin in parts.Plugins)
{
this.applicationApi.Plugins.Add(plugin);
plugin.Connect(this.applicationApi);
}
}
catch
{
// ERR: handle error
}
}
即使 Microsoft.Threading.Tasks.dll
和相关程序集文件存在于 Plugins 文件夹中,但主应用程序 bin 文件夹中没有,为什么 MEF 无法加载插件?有什么方法可以让 MEF 在 Plugins 文件夹中搜索程序集依赖项?
拥有插件模型意味着我无法预料插件可能引用了哪些程序集,因此我无法将它们包含在应用程序的主 bin 文件夹中,这就是为什么我希望所有相关的插件和插件依赖项都在插件文件夹。
您在支持第 3 方插件时遇到了 运行 一个基本问题。您的问题是,当您加载插件时,运行time 将在需要时仅在您的 AppDomain 已知的指定文件夹中搜索其引用。那就是 WorkingDirectory of that process, then path etc。
本质上,您正在加载一个需要 System.Threading.Tasks
的插件。该 DLL 位于您的 /Plugin
文件夹中。当 .net 加载您的插件时,它将搜索该程序集但无法找到它,因为它位于 /Plugin
文件夹中并且失败了。
有几个解决方案。
- 不要使用插件文件夹
这将是最简单的解决方案,当所有程序集(包括第 3 方库的引用)都位于您的 WorkingDirectory 中时,.net 将毫不费力地找到该插件的所有引用。
- 将插件文件夹添加到您的探测路径
这将扩展 .net 搜索第 3 方参考的路径: https://docs.microsoft.com/en-us/dotnet/framework/deployment/how-the-runtime-locates-assemblies#locating-the-assembly-through-probing
- 为每个插件使用 AppDomains。
AppDomain 是我的选择,因为它不仅允许您在其 "own" 容器中加载程序集,而且还可以仅为插件模拟工作目录。例如,如果其中一个插件使用与您的应用程序相同的框架但版本不同,这会派上用场。
- 自行加载依赖
这就是解决这个问题的 "straight forward" 方法。您可以将每个程序集加载为 ReflectionOnly,确定所有依赖项,然后加载它们。这几乎可以保证工作。
4.1。 AssemblyResolve 事件
这只是 "redirect" .net 从 PluginFolder 加载程序集的另一种方式 https://docs.microsoft.com/en-us/dotnet/api/system.appdomain.assemblyresolve?view=netframework-4.8
编辑:
AssemblyCatalog 也有一定的问题,它使用 Assembly.Load
而不是 Assembly.LoadFrom
来加载给定的程序集。这是您的问题的重要组成部分,因为 LoadFrom
会探测程序集的来源路径以获取其依赖项,而 Load
则不会。
https://github.com/JPVenson/MSEF/blob/master/JPB.Shell/JPB.Shell.MEF/Model/StrongNameCatalog.cs
您可以使用像这样使用 LoadFrom 的目录。 免责声明:我是该项目的创建者。