Finally, I finished up with almost the same solution that @jbl suggested.
I ve created a filter which stores my current user into the job parameters.
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));
}
Then I ve replaced current job activator
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();
}
}
And finally, set current user details (which is null on the moment of running task)
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);
}
}
Then replace the existing JobActivator in container
services.Replace(new ServiceDescriptor(typeof(JobActivator), typeof(CustomJobActivator), ServiceLifetime.Scoped));
public interface ICurrentUser
{
string UserId { get; set; }
}
public class UserProvider : ICurrentUser
{
private int? _userId;
protected readonly IHttpContextAccessor HttpContextAccessor;
public UserProvider(IHttpContextAccessor httpContextAccessor)
{
HttpContextAccessor = httpContextAccessor;
}
public virtual int? UserId =>
HttpContextAccessor.HttpContext?.User?.Id() != null &&
HttpContextAccessor.HttpContext?.User?.Id() != default(int)
? HttpContextAccessor.HttpContext?.User?.Id(): _userId;
}
UserProvider is scoped service.
之后,当服务从这个范围开始解决时,当我使用国际志愿人员组织进行适当工作时,他们就会在DbContext和其他地方获得用户背景和所有过滤器。