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

问题描述 投票:1回答:2

我在我的Web Api项目中使用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尝试启动作业时,我收到以下异常:

无法加载文件或程序集“App.Plugins.FirstPlugin,Version = 1.0.0.0,Culture = neutral,PublicKeyToken = null”或其依赖项之一。该系统找不到指定的文件。

在这种情况下,甚至可以一起使用MEF和Hangfire吗?如果是,那么正确的程序是什么?

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

c# asp.net-web-api mef hangfire
2个回答
1
投票

您需要通过订阅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加载到当前的应用程序域中,所以你必须帮助它。


0
投票

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

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

© www.soinside.com 2019 - 2024. All rights reserved.