English 中文(简体)
4. 夜射与用户背景
原标题:Hangfire run background job with user context
  • 时间:2019-09-25 15:13:29
  •  标签:
  • hangfire

I have an app, with multi-tenancy. I want to create background job under user context, but I can t find good way to implement that. I ll explain a bit my architecture. I m using Interface ICurrentUser that contain UserID. In Startup class I register as scoped in IoC the class WebUser which implements ICurrentUser, this class getting HttpContext and extract user details from claims.

I m 执行背景工作和ICurrentUser。 用户信息数据库没有像预期的那样无效,因为hang火没有任何httpcontext。

I m solving this problem by creating my background tasks with method which accept ICurrentUser as first argument, then inside method body, I set my "CurrentUser" for UnitOfWork (and AppServices) and start executing task, the problem with this approach that I have to repeat this code with every background task and pass CurrentUser into it.

My question how can achieve next thing. Or maybe you can suggest other solutions for it.

  1. How can I pass my CurrentUser into JobActivator, to order I can setup user context before all services is resolved.

For Example it may look like that:

BackgroundJob.Enqueue<MySvc>(UserContext, mysvc=>mysvc.Run());

I read sources and really didn t find any extension points to implement this.

非常感谢任何帮助。

问题回答

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和其他地方获得用户背景和所有过滤器。

这一实例表明,这一平台无法进行序列化和淡化。

我在试图将用户(ICurrentUser)确定为工作参数时,会发现这一错误。





相关问题
4. 夜射与用户背景

I have an app, with multi-tenancy. I want to create background job under user context, but I can t find good way to implement that. I ll explain a bit my architecture. I m using Interface ...

热门标签