简体   繁体   中英

Adding Hangfire Job Filter to ASP.NET Web Controller Method Fails to be Caught

I have created a custom filter to catch failed jobs. This works fine when adding it via the GlobalJobFilters class. What I am looking for is to implement the filter on a method of my API controller, to catch only jobs within that method that fail. How can I register the filter to do this?

I've tried adding it to the interface of the DeleteDrawing class which is located in another project, but that did not work either.

The filter:

using MyHangfire.Core.Jobs;
using Hangfire;
using Hangfire.Common;
using Hangfire.States;
using Hangfire.Storage;

namespace MyHangfire.Web.Filters
{
    public class DeleteDrawingFailedFilterAttribute : JobFilterAttribute, IApplyStateFilter
    {
        public void OnStateApplied(ApplyStateContext context, IWriteOnlyTransaction transaction)
        {
            BackgroundJobClient backgroundJobClient = new BackgroundJobClient();
            EmailJob emailJob = new EmailJob();

            FailedState failedState = context.NewState as FailedState;

            if (failedState != null)
            {
                string name = context.BackgroundJob.Job.Args[Array.IndexOf(context.BackgroundJob.Job.Method.GetParameters(), "name")].ToString();
                string email = context.BackgroundJob.Job.Args[Array.IndexOf(context.BackgroundJob.Job.Method.GetParameters(), "email")].ToString();
                string filename = context.BackgroundJob.Job.Args[Array.IndexOf(context.BackgroundJob.Job.Method.GetParameters(), "filename")].ToString();
                Dictionary<string, string> recipients = new Dictionary<string, string>() { { name, email } };

                string subject = "Delete Drawing Job Failed!";
                string body = 
$@"Hello,

The delete drawing job {context.Job.Method.Name} for part number {filename.Substring(0, filename.IndexOf('.'))} has failed.
Please manually complete this and the remaining jobs found on the Hangfire dashboard and delete them from the queue.

Thanks,

Hangfire";

                backgroundJobClient.ContinueJobWith(context.BackgroundJob.Id, () => emailJob.CreateAndSendMessage(recipients, subject, body), JobContinuationOptions.OnAnyFinishedState);
            }
        }

        public void OnStateUnapplied(ApplyStateContext context, IWriteOnlyTransaction transaction)
        {
            throw new NotImplementedException();
        }
    }
}

The controller:

using MyHangfire.Core.Jobs;
using MyHangfire.Core.Jobs.DeleteDrawingJob;
using MyHangfire.Web.Filters;
using MyHangfire.Web.Models;
using Hangfire;
using Microsoft.AspNetCore.Mvc;

// For more information on enabling Web API for empty projects, visit https://go.microsoft.com/fwlink/?LinkID=397860

namespace MyHangfire.Web.Controllers
{
    [Route("api/[controller]")]
    [ApiController]
    public class JobsController : ControllerBase
    {
        private readonly IBackgroundJobClient _backgroundJobClient;
        private readonly IPrintJob  _printJob;
        private readonly IDeleteDrawing _deleteDrawing;
        private readonly IEmailJob _emailJob;
        private readonly IConfiguration _configuration;
        
        public JobsController(
            IBackgroundJobClient backgroundJobClient,
            IPrintJob printJob,
            IDeleteDrawing deleteDrawing,
            IEmailJob emailJob,
            IConfiguration configuration)
        {
            _backgroundJobClient = backgroundJobClient;
            _printJob = printJob;
            _deleteDrawing = deleteDrawing;
            _emailJob = emailJob;
            _configuration = configuration;
        }
        
        // POST api/<JobsController>
        [HttpPost]
        public void Post()
        {
            _backgroundJobClient.Enqueue(() => _printJob.Run());
        }

        // POST api/<JobsController>
        [HttpPost("deletedrawing")]
        [DeleteDrawingFailedFilter]
        [AutomaticRetry(Attempts = 0)]
        public void Post([FromBody] DeleteDrawingModel ddModel)
        {
            string pdfGUID = _configuration.GetSection("Service Accounts").GetValue<string>("SA_PDF");
            string sqlGUID = _configuration.GetSection("Service Accounts").GetValue<string>("SA_SQL");

            string jobID = _backgroundJobClient.Enqueue(() => _deleteDrawing.DeleteSLDDRW(ddModel.Name, ddModel.Email, ddModel.Filename, ddModel.FolderID, ddModel.VaultName, sqlGUID));
            jobID = _backgroundJobClient.ContinueJobWith(jobID, () => _deleteDrawing.DeletePDF(ddModel.Name, ddModel.Email, ddModel.Filename, ddModel.FileDirectory, ddModel.RestoreDirectory, pdfGUID), JobContinuationOptions.OnlyOnSucceededState);
            jobID = _backgroundJobClient.ContinueJobWith(jobID, () => _deleteDrawing.UpdateXARecord(ddModel.Name, ddModel.Email, ddModel.Filename, ddModel.DBLibrary, sqlGUID), JobContinuationOptions.OnlyOnSucceededState);

            //Send email on success for all above jobs.
            Dictionary<string, string> recipients = new Dictionary<string, string>() { { ddModel.Name, ddModel.Email } };
            //foreach (KeyValuePair<string, string> recipient in _configuration.GetSection("Email Lists").GetSection("DeleteDrawing").GetChildren().ToList<KeyValuePair<string, string>>())
            //{
            //    recipients.Add(recipient.Key, recipient.Value);
            //}


            string subject = "Delete Drawing Job Succeeded!";
            string body = 
$@"Hello,

The delete drawing jobs for part number {ddModel.Filename.Substring(0, ddModel.Filename.IndexOf('.'))} have completed successfully.

Thanks,

Hangfire";

            jobID = _backgroundJobClient.ContinueJobWith(jobID, () => _emailJob.CreateAndSendMessage(recipients, subject, body), JobContinuationOptions.OnlyOnSucceededState);
        }
    }
}

Program.cs

using MyHangfire.Core.Jobs;
using MyHangfire.Core.Jobs.DeleteDrawingJob;
using MyHangfire.Web;
using MyHangfire.Web.Filters;
using Hangfire;
using Hangfire.MemoryStorage;

var builder = WebApplication.CreateBuilder(args);

// Add services to the container.
builder.Services.AddRazorPages();
builder.Services.AddControllers();
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();

//Hangfire config
builder.Services.AddHangfire(config =>
    config.SetDataCompatibilityLevel(CompatibilityLevel.Version_170)
    .UseSimpleAssemblyNameTypeSerializer()
    .UseDefaultTypeSerializer()
    .UseMemoryStorage()
);

//GlobalJobFilters.Filters.Add(new DeleteDrawingFailedFilterAttribute());

builder.Services.AddHangfireServer();
builder.Services.AddSingleton<ITriggers, Triggers>();
builder.Services.AddTransient<IEmailJob, EmailJob>();
builder.Services.AddTransient<IPrintJob, PrintJob>();
builder.Services.AddTransient<IDeleteDrawing, DeleteDrawing>();

builder.Services.AddSingleton<IConfiguration>(builder.Configuration);

var app = builder.Build();

// Show hangfire dashboardon 
app.UseHangfireDashboard();

app.UseSwagger();
app.UseSwaggerUI();


if (!app.Environment.IsDevelopment())
{
    // Run Triggers In Production Environment
    app.Services.GetService<ITriggers>()
        ?.SetupProd();

    // Configure the HTTP request pipeline.
    app.UseExceptionHandler("/Error");
    // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
    app.UseHsts();
}
else
{
    // Run Triggers in Dev Environment
    app.Services.GetService<ITriggers>()
        ?.SetupDev();
}

app.UseHttpsRedirection();
app.UseStaticFiles();

app.UseRouting();

app.UseAuthorization();

app.MapRazorPages();
app.UseEndpoints(x => x.MapControllers());

app.Run();

Any help will be appreciated.

So, apparently I needed to decorate the DeleteDrawing class, not the interface, and then it worked. This confuses me more now, since according to the documentation I would have needed to decorate the interface.

Link: https://docs.hangfire.io/en/latest/extensibility/using-job-filters.html

If your service is resolved via interface, then the filter should be applied to your interface instead of class:

 [LogEverything] public interface IEmailService { [LogEverything] void Send() { } }

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM