Hello, I have troubles with scoped dependency injection and nested background jobs.
I have some background work (like workflow), which may run nested background jobs (like sending notifications). And I need some scoped context (like a CurrentUser).
For background jobs launched from web controller - scope, obviously, is http context.
For nested background jobs launched from root background job - scope should be scope of the parent background job.
So, I implemented the filter
public class MyBackgroundJobStarter
{
public MyBackgroundJobStarter(IServiceProvider serviceProvider)
: base(serviceProvider)
{
}
public override void OnCreating(CreatingContext filterContext)
{
var httpContextAccessor = _serviceProvider.GetRequiredService<IHttpContextAccessor>();
if (httpContextAccessor.HttpContext != null)
{
var currentUser = httpContextAccessor.HttpContext.RequestServices.GetRequiredService<ICurrentUser>();
filterContext.SetJobParameter("BackgroundJobUser", currentUser);
}
else
{
// We can't get scope for nested background job here.
// _serviceProvider is root service provider, not scoped in MyBackgroundJobActivator.BeginScope
// _serviceProvider.GetRequiredService<ICurrentUser>();
}
}
}
And job activator
public class MyBackgroundJobActivator : JobActivator
{
protected readonly IServiceScopeFactory _serviceScopeFactory;
public BackgroundJobActivator(IServiceScopeFactory serviceScopeFactory)
{
if (serviceScopeFactory == null)
{
throw new ArgumentNullException("serviceScopeFactory");
}
_serviceScopeFactory = serviceScopeFactory;
}
public override JobActivatorScope BeginScope(JobActivatorContext context)
{
IServiceScope serviceScope = _serviceScopeFactory.CreateScope();
SetupScope(context, serviceScope);
return new BackgroundJobActivatorScope(serviceScope);
}
protected virtual void SetupScope(JobActivatorContext context, IServiceScope serviceScope)
{
var backgroundJobUser = context.GetJobParameter<BackgroundJobUser>("BackgroundJobUser");
if (backgroundJobUser is not null)
{
var currentUser = serviceScope.ServiceProvider.GetRequiredService<ICurrentUser>();
currentUser.ReplaceBy(backgroundJobUser);
}
}
public override JobActivatorScope BeginScope(PerformContext context)
{
IServiceScope serviceScope = _serviceScopeFactory.CreateScope();
SetupScope(context, serviceScope);
return new BackgroundJobActivatorScope(serviceScope);
}
protected virtual void SetupScope(PerformContext context, IServiceScope serviceScope)
{
var backgroundJobUser = context.GetJobParameter<BackgroundJobUser>("BackgroundJobUser");
if (backgroundJobUser is not null)
{
var currentUser = serviceScope.ServiceProvider.GetRequiredService<ICurrentUser>();
currentUser.ReplaceBy(backgroundJobUser);
}
}
}
It works fine when I run job from web controller.
But when I run job from parent job, it doesn’t work, because I can’t get serviceScope created in MyBackgroundJobActivator.BeginScope for parent job in the MyBackgroundJobStarter.OnCreating for the nested job.
So, please advise how to deal with that? Thanks.