Home > Software design >  Scoped service in Hangfire Job Filter
Scoped service in Hangfire Job Filter

Time:04-05

After searching & reading possibly every discussion related to the topic in question, there seemed not to be a clear (or even unclear) solution to this.

I have a job filter that I want to apply to every job. What I want to do is: when the job fails (goes to FailedState, which happens when the max retry attemps exceed OR it is thrown in that state manually), I want to log a custom, user-friendly exception message to the database (my custom table).

public class WorkflowJobFailureAttribute : JobFilterAttribute, IApplyStateFilter {

    public WorkflowJobFailureAttribute(IServiceProvider serviceProvider) {
        // Getting my required services here
    }

    public void OnStateApplied(ApplyStateContext context, IWriteOnlyTransaction transaction) 
    {
        var failedState = context.NewState as FailedState;
        if (failedState != null) {
            // - HANDLE FAILED STATE HERE -
                
            // You can get the exception (which is of type `Exception`) using: `failedState.Exception`
        }
    }
}

But every darn example/suggestion I was was about registering the filter globally (GlobalJobFilters.Filters.Add(new WorkflowJobFailureAttribute(serviceProvider)); in Startup.cs). But this is the ROOT service provider, so it will not work with scoped lifetime.

There is a possible 'workaround', suggested by balazs hideghety in the comments here. But before diving deep into that (because it feels like a 'distant' solution), I would like to know: has anyone, in his experience, solved this problem?

CodePudding user response:

Inject a scoped service provider factory and then create the scoped service provider as needed

public class WorkflowJobFailureAttribute : JobFilterAttribute, IApplyStateFilter {
    private readonly IServiceScopeFactory scopeFactory;

    public WorkflowJobFailureAttribute(IServiceScopeFactory scopeFactory) {
        this.scopeFactory = scopeFactory; 
    }

    public void OnStateApplied(ApplyStateContext context, IWriteOnlyTransaction transaction) {
        var failedState = context.NewState as FailedState;
        if (failedState != null) {
            using (IServiceScope scope = scopeFactory.CreateScope()) {
                IServiceProvider serviceProvider = scope.ServiceProvider;

                // Getting my required services here

                // - HANDLE FAILED STATE HERE -
                
                // You can get the exception (which is of type `Exception`) using: `failedState.Exception`
            }
        }
    }
}

In the preceding code, an explicit scope is created and the service provider can be used to resolve scoped services.

  • Related