Hangfire: Can you use another mechanism rather then attributes?

Created on 29 Sep 2016  路  9Comments  路  Source: HangfireIO/Hangfire

For instance, we would like to determine this at runtime rather than as an attribute decoration in source code.

  • Concurrent vs Nonconcurrent
  • Queue Name

We would like to be able to define for a specific job which queue it should run in, whether it is concurrent (in that queue) or can run in parallel and other attributes that we currently have to provide before the job is even compiled.

If this was answered somewhere else, would you point me there please? So far I've not found it if it exists.

TIA

question

Most helpful comment

You can define your own implementation of the IJobFilterProvider interface and register it:

public class MyFilterProvider : IJobFilterProvider
{
    public IEnumerable<JobFilter> GetFilters(Job job)
    {
        if (job.Type.Namespace.StartsWith("ConsoleApp33"))
        {
            return new[]
            {
                new JobFilter(new AutomaticRetryAttribute(), JobFilterScope.Global, order: 30),
            };
        }

        return Enumerable.Empty<JobFilter>();
    }
}

// Call this startup configuration
JobFilterProviders.Providers.Add(new MyFilterProvider());

All 9 comments

I agreed with @KeithBarrows , especially for queue name. As it's difficult to extend attribute with complex behavior.

It would be nice to add a Nonconcurrent ability to a queue instead of a job. Just a thought I had driving to work today. :)

I agree about this idea: attribute-only configuration/filters means we cannot set configuration at runtime. It also make separation of concerns difficult.

How about a way to add jobParameters at job creation so we could write our own runtime attributes?

Yeah, passing a queue name when enqueuing the job (to be able to set its priority dynamically), as (optional) parameters (or a parameter object) to the enqueue method would be great. Also, for recurring jobs. I currently have exactly that problem.

I also agree with all things mentioned above. We built our application abstracting from Hangfire as much as possible, but the attributes are giving me some headaches. I end up doing strange things using job parameters and global filters rather than just passing the dynamic info I have at runtime to apply to a specific job.

You can define your own implementation of the IJobFilterProvider interface and register it:

public class MyFilterProvider : IJobFilterProvider
{
    public IEnumerable<JobFilter> GetFilters(Job job)
    {
        if (job.Type.Namespace.StartsWith("ConsoleApp33"))
        {
            return new[]
            {
                new JobFilter(new AutomaticRetryAttribute(), JobFilterScope.Global, order: 30),
            };
        }

        return Enumerable.Empty<JobFilter>();
    }
}

// Call this startup configuration
JobFilterProviders.Providers.Add(new MyFilterProvider());

I specify queue name via code like so:

   var client = new BackgroundJobClient();
   var queueName = "myqueu";
   var state = new EnqueuedState(queueName);
   client.Create<JobClass>(xx => xx.JobMethod(parm), state);               

If you need to alter the behaviour of a job at runtime the filters are the way to go (AFAIK). I created a new filter attribute, derived from JobFilterAttribute like shown in the documentation.

Within each step you can different aspects of the job, but be aware that there are already other job filters already registered within the system, which you like to be run before or after your own filter. Examples are the QueueAttribute which should normally be run before your own filter or the ContinuationsSupportAttribute that takes care for awaiting jobs and should normally be run after your own filter. So as a first tip, your constructor should be looking something like this:

public class MyFilterAttribute : JobFilterAttribute, IClientFilter, IServerFilter, IElectStateFilter, IApplyStateFilter
{
    private static readonly int DefaultOrder = new ContinuationsSupportAttribute().Order - 1;

    public MyFilterAttribute()
    {
    }
}

Within the OnCreating() method you can manipulate the InitialState property of the CreatingContext. It already contains some state (normally EnqueuedState or ScheduledState). In case of the EnqueuedState you could replace the queue name with whatever you like:

public void OnCreating(CreatingContext context)
{
    var enqueuedState = context.InitialState as EnqueuedState;

    if (enqueuedState == null)
        return;

    var (queueName, reason) = DetermineQueue(context.Job);
    enqueuedState.Queue = queueName;
    enqueuedState.Reason = reason;
}

private (string queueName, string reason) DetermineQueue(Job job)
{
    return job.Method.Name.Length % 2 == 0
        ? ("even", "In even queue cause method name implies it.")
        : ("odd", "In odd queue, cause I like so.");
}

`using Hangfire.Common;
using Hangfire.States;
using IntegraServicos.Domain.Enums;
using System;
using System.Linq;

namespace IntegraServicos.Helper.HangFire
{
public class SelectQueueAttribute : JobFilterAttribute, IElectStateFilter
{

    public void OnStateElection(ElectStateContext context)
    {
        var enqueuedState = context.CandidateState as EnqueuedState;
        if (enqueuedState != null)
        {
            enqueuedState.Queue = DetermineQueue(context.BackgroundJob.Job);
        }
    }

    String DetermineQueue(Job job)
    {
        var query = String.Empty;
        try
        {
            var args = job.Args.ToList();
            if (args.Count > 1)
            {
                query = QueueServiceName.Services.Where(x => x.Contains(args[0].ToString().Replace("-", "_").ToLower())).FirstOrDefault().ToLower();
            }
            else
            {
                query = QueueServiceName.Services.Where(x => x.Contains(args.Cast<ServiceCallModel>().ToList().FirstOrDefault().ServiceKey.Replace("-", "_").ToLower())).FirstOrDefault().ToLower();
            }
        }
        catch (Exception)
        {
            Console.WriteLine("Nenhum fila encontrada no argumento, sera enviado para a default");
        }
        return query ?? "default";
    }
}

}
`

Was this page helpful?
0 / 5 - 0 ratings

Related issues

JvanderStad picture JvanderStad  路  3Comments

thurfir picture thurfir  路  4Comments

dbones picture dbones  路  3Comments

dealproc picture dealproc  路  3Comments

nsnail picture nsnail  路  3Comments