HangfireIO / Hangfire

An easy way to perform background job processing in .NET and .NET Core applications. No Windows Service or separate process required
https://www.hangfire.io
Other
9.36k stars 1.7k forks source link

Can you use another mechanism rather then attributes? #680

Open KeithBarrows opened 8 years ago

KeithBarrows commented 8 years ago

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

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

tonidy commented 8 years ago

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

KeithBarrows commented 8 years ago

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. :)

thurfir commented 8 years ago

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?

gingters commented 7 years ago

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.

MaQy commented 7 years ago

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.

odinserj commented 7 years ago

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());
SimonOrdo commented 5 years ago

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);               
olivermue commented 5 years ago

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.");
}
willardanuy commented 4 years ago

`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";
    }
}

} `