MEF没有检测到插件依赖性

Dea*_*unz 10 .net c# plugins mef

我有MEF和使用插件文件夹的问题.

我有一个主应用程序,通过MEF支持插件.主应用程序不引用包含.NET任务类型的程序集用于多线程,但是一个或多个插件可以.

插件位于Plugins文件夹中,我正在使用DirectoryCatalog.

我一直ReflectionTypeLoadException被MEF抛出

无法加载一个或多个请求的类型.检索LoaderExceptions属性以获取更多信息.

LoaderExceptions属性包含一个 FileNotFoundException

"无法加载文件或程序集'System.Threading.Tasks,Version = 1.5.11.0,Culture = neutral,PublicKeyToken = b03f5f7f11d50a3a'或其中一个依赖项.系统找不到指定的文件.":"System.Threading.Tasks ,Version = 1.5.11.0,Culture = neutral,PublicKeyToken = b03f5f7f11d50a3a"

该插件System.Threading.Tasks通过Microsoft NuGet包引用引用.

这是我的助手方法:

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;
}
Run Code Online (Sandbox Code Playgroud)

这是我用来调用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
    }
}
Run Code Online (Sandbox Code Playgroud)

为什么即使Microsoft.Threading.Tasks.dll插件文件夹中存在相关的汇编文件而不是主应用程序bin文件夹,MEF也无法加载插件?有没有办法告诉MEF在Plugins文件夹中搜索程序集依赖项?

有一个插件模型意味着我无法预测插件可能引用的程序集,因此我不能将它们包含在应用程序的主bin文件夹中,这就是为什么我希望所有相关的插件和插件依赖项都在插件文件夹中.

Ven*_*son 1

在支持第三方插件时,您遇到了一个重要问题。您的问题是,当您加载插件时,运行时将在需要时仅在 AppDomain 已知的指定文件夹中搜索其引用。那将是该进程的工作目录,然后是路径等

本质上,您正在加载一个需要System.Threading.Tasks. 该 DLL 位于您的/Plugin文件夹中。当 .net 加载您的插件时,它将搜索该程序集,但无法找到它,因为它位于文件夹内/Plugin并且失败。

有几个解决方案。

  1. 不要使用插件文件夹

这将是最简单的解决方案,当所有程序集(包括第 3 方库的引用)都位于您的工作目录中时,.net 将轻松找到该插件的所有引用。

  1. 将 Plugin 文件夹添加到您的探测路径

这将扩展 .net 将搜索第 3 方参考的路径: https: //learn.microsoft.com/en-us/dotnet/framework/deployment/how-the-runtime-locates-assemblies# located-the- assembly-直通探测

  1. 对每个插件使用 AppDomains。

我在这里选择 AppDomain,因为它不仅允许您将程序集加载到其“自己的”容器中,而且还可以仅模拟插件的工作目录。例如,如果某个插件使用与您的应用程序相同的框架但版本不同,那么这会很方便。

  1. 自己加载依赖

这将是解决这个问题的“直接”方法。您可以将每个程序集加载为ReflectionOnly,确定所有依赖项,然后加载它们。这几乎保证了工作。

4.1. AssemblyResolve 事件

这只是“重定向”.net 以从 PluginFolder 加载程序集的另一种方法 https://learn.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 的目录。免责声明:我是该项目的创建者。