使用MEF加载插件时如何使用Hangfire?

How to use Hangfire when using MEF to load plugins?

我在我的 WebApi 项目中使用 MEF 来加载位于 bin 文件夹以外的文件夹中的插件。我执行以下操作:

var directoryCatalog = new DirectoryCatalog(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "plugins"));

var container = new CompositionContainer(directoryCatalog);
container.ComposeParts();

IList<IPlugin> plugins = container.GetExportedValues<IPlugin>().ToList();

然后我通过以下方式设置插件:

plugins[0].Startup(logService, unitOfWork);

注意:我没有使用 [ImportingConstructor] 来传递上述依赖项,因为我使用的是已经在控制器中实例化的现有实例。

然后我安排作业如下:

BackgroundJob.Schedule(() => plugins[0].Start(), new TimeSpan(0, 0, 1));

但是,当 Hangfire 尝试启动作业时出现以下异常:

Could not load file or assembly 'App.Plugins.FirstPlugin, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null' or one of its dependencies. The system cannot find the file specified.

在这种情况下,MEF 和 Hangfire 可以同时使用吗?如果是,正确的程序是什么?

注意: 如果插件 DLL 位于与主应用程序相同的 bin 文件夹中,则 Hangfire 可以正常工作。但这违背了拥有一个单独的插件文件夹的目的。

Hangfire Extensions 页面上,有一个 NuGet 包的概念 Hangfire.MEF.

它导致Hangfire.MEF - Hangfire background job activator based on MEF IoC Container。我认为它可能会帮助您解决问题。

您需要通过订阅 AppDomain.AssemblyResolve 事件(在启动 hangfire 服务器之前的某处)来引导 hangfire(或者更确切地说 - .NET 本身)以定位此程序集:

var pluginPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "plugins");
AppDomain.CurrentDomain.AssemblyResolve += (sender, args) => {
    var asmName = new AssemblyName(args.Name);
    var plugin = Path.Combine(pluginPath, asmName.Name + ".dll");
    if (File.Exists(plugin))
        return Assembly.LoadFrom(plugin);
    return null;
};

由于您的插件实际上已经加载到当前应用程序域(通过 MEF),因此以下内容也应该有效(我认为可能比上面更好):

AppDomain.CurrentDomain.AssemblyResolve += (sender, args) => {
    var asmName = new AssemblyName(args.Name);
    var existing = AppDomain.CurrentDomain.GetAssemblies().FirstOrDefault(c => c.FullName == asmName.FullName);
    if (existing != null) {
        return existing;
    }
    return null;
};

这是必要的,因为 hangfire 要从数据库中的状态反序列化您的插件实例,使用 Type.GetType 提供程序集限定名称,并且由于某些复杂性(我认为)与这个问题无关 - Type.GetType 不会找到你的插件程序集,即使它已经被 MEF 加载到当前的应用程序域中,所以你必须帮助它一点。