hangfire - Hangfire 使用用户上下文运行后台作业
问题描述
我有一个多租户的应用程序。我想在用户上下文下创建后台作业,但我找不到实现它的好方法。我将解释一下我的架构。我正在使用包含 UserID 的接口 ICurrentUser。在 Startup 类中,我在 IoC 中注册了实现 ICurrentUser 的类 WebUser,该类获取 HttpContext 并从声明中提取用户详细信息。
我正在执行后台作业,并且 ICurrentUser.UserID 按预期为空,因为hangfire 没有任何httpcontext。
我通过使用接受 ICurrentUser 作为第一个参数的方法创建我的后台任务来解决这个问题,然后在方法主体内,我为 UnitOfWork(和 AppServices)设置我的“CurrentUser”并开始执行任务,我有这种方法的问题对每个后台任务重复此代码并将 CurrentUser 传递给它。
我的问题如何才能实现下一件事。或者,也许您可以建议其他解决方案。
- 如何将我的 CurrentUser 传递给 JobActivator,以便在解决所有服务之前设置用户上下文。
例如,它可能看起来像这样:
BackgroundJob.Enqueue<MySvc>(UserContext, mysvc=>mysvc.Run());
我阅读了资料,并没有找到任何扩展点来实现这一点。
任何帮助是极大的赞赏。
解决方案
最后,我完成了与@jbl 建议的几乎相同的解决方案。我创建了一个过滤器,将我当前的用户存储到作业参数中。
public class BackgroundJobFilter : JobFilterAttribute, IClientFilter, IApplyStateFilter
{
private readonly IServiceProvider _serviceProvider;
public BackgroundJobFilter(IServiceProvider serviceProvider)
{
_serviceProvider = serviceProvider;
}
public void OnCreating(CreatingContext filterContext)
{
var currentUser = _serviceProvider.GetRequiredService<ICurrentUser>();
filterContext.SetJobParameter(nameof(ICurrentUser), currentUser);
}
}
然后将过滤器添加到 Hangfire
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
GlobalConfiguration.Configuration.UseFilter(new BackgroundJobFilter(app.ApplicationServices));
}
然后我替换了当前的工作激活器
internal class ServiceJobActivatorScope : JobActivatorScope
{
private readonly IServiceScope _serviceScope;
public ServiceJobActivatorScope([NotNull] IServiceScope serviceScope)
{
if (serviceScope == null)
throw new ArgumentNullException(nameof(serviceScope));
_serviceScope = serviceScope;
}
public override object Resolve(Type type)
{
return ActivatorUtilities.GetServiceOrCreateInstance(_serviceScope.ServiceProvider, type);
}
public override void DisposeScope()
{
_serviceScope.Dispose();
}
}
最后,设置当前用户详细信息(在运行任务时为空)
public class CustomJobActivator : JobActivator
{
private readonly IServiceScopeFactory _serviceScopeFactory;
private readonly IMapper _objectMapper;
public CustomJobActivator([NotNull] IServiceScopeFactory serviceScopeFactory, IMapper objectMapper)
{
if (serviceScopeFactory == null)
throw new ArgumentNullException(nameof(serviceScopeFactory));
_serviceScopeFactory = serviceScopeFactory;
_objectMapper = objectMapper;
}
public override JobActivatorScope BeginScope(JobActivatorContext context)
{
var user = context.GetJobParameter<WebUser>(nameof(ICurrentUser));
var serviceScope = _serviceScopeFactory.CreateScope();
var currentUser = serviceScope.ServiceProvider.GetRequiredService<ICurrentUser>();
//Copy value from user to currentUser
_objectMapper.Map(user, currentUser);
return new ServiceJobActivatorScope(serviceScope);
}
}
然后替换容器中已有的 JobActivator
services.Replace(new ServiceDescriptor(typeof(JobActivator), typeof(CustomJobActivator), ServiceLifetime.Scoped));
之后,当服务开始从此范围解析时,当我使用 ICurrentUser 正常工作时,它们将获得用户上下文和 DbContext 和其他地方的所有过滤器。
推荐阅读
- node.js - JWT 认证失败
- javascript - Electron,如何使 100% 确定渲染器从主服务器接收消息?
- c# - 如何在 WPF 中的 ReactiveUI 中设置 ItemsSource 和源集合之间的双向绑定
- swift - UITextField 的 leftView 上的图像未呈现
- swagger - 无法解析 yaml 文件中 requestBody 中定义的参考组件架构
- python - 优化:根据日期将带有DatetimeIndex的pandas DataFrame拆分为列表
- javascript - 如何在 OnChangeText 中向我的组件 inputText 添加两个状态
- javascript - learnyounode 9#上这两种解决方案有什么区别?
- react-native - React Native 仅显示模式/屏幕一次
- matlab - 如何找到一个逻辑值来发现日期是否具有相同的月份和年份?