You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

为ASP.NET Web控制器方法添加Hangfire作业过滤器未生效的求助

问题解决:Hangfire自定义过滤器仅应用于特定Job

你遇到的核心问题是:Hangfire的Job过滤器不能直接标记在API控制器方法上——控制器方法处理的是HTTP请求,而Hangfire过滤器是作用于后台Job执行流程的。要实现仅捕获特定Job的失败,有两种可靠方案:


方案一:创建Job时显式指定过滤器

在调用Enqueue或ContinueJobWith时,手动为目标Job添加过滤器,精准控制作用范围。

修改控制器中Post([FromBody] DeleteDrawingModel ddModel)方法的Job创建逻辑:

// 替换原Enqueue代码,显式添加过滤器
var deleteSLDDRWJob = Job.FromExpression(() => _deleteDrawing.DeleteSLDDRW(
    ddModel.Name, ddModel.Email, ddModel.Filename, ddModel.FolderID, ddModel.VaultName, sqlGUID));
deleteSLDDRWJob.Filters.Add(new DeleteDrawingFailedFilterAttribute());
string jobID = _backgroundJobClient.Enqueue(deleteSLDDRWJob);

// 后续的ContinueJobWith同理,给每个要监控的Job添加过滤器
var deletePDFJob = Job.FromExpression(() => _deleteDrawing.DeletePDF(
    ddModel.Name, ddModel.Email, ddModel.Filename, ddModel.FileDirectory, ddModel.RestoreDirectory, pdfGUID));
deletePDFJob.Filters.Add(new DeleteDrawingFailedFilterAttribute());
jobID = _backgroundJobClient.ContinueJobWith(jobID, deletePDFJob, JobContinuationOptions.OnlyOnSucceededState);

var updateXARecordJob = Job.FromExpression(() => _deleteDrawing.UpdateXARecord(
    ddModel.Name, ddModel.Email, ddModel.Filename, ddModel.DBLibrary, sqlGUID));
updateXARecordJob.Filters.Add(new DeleteDrawingFailedFilterAttribute());
jobID = _backgroundJobClient.ContinueJobWith(jobID, updateXARecordJob, JobContinuationOptions.OnlyOnSucceededState);

方案二:将过滤器标记在Job实现方法上

如果希望所有调用DeleteDrawing类中特定方法的Job都自动应用过滤器,可以把过滤器直接加在实现类的方法上(注意:接口方法上标记过滤器无效,必须加在具体实现)。

假设DeleteDrawing类在MyHangfire.Core.Jobs.DeleteDrawingJob命名空间下,修改其方法:

using MyHangfire.Web.Filters; // 需引用Web项目的过滤器,或把过滤器移到Core项目
using Hangfire;

namespace MyHangfire.Core.Jobs.DeleteDrawingJob
{
    public class DeleteDrawing : IDeleteDrawing
    {
        [DeleteDrawingFailedFilter] // 直接标记在实现方法上
        [AutomaticRetry(Attempts = 0)]
        public void DeleteSLDDRW(string name, string email, string filename, int folderID, string vaultName, string sqlGUID)
        {
            // 原方法逻辑
        }

        [DeleteDrawingFailedFilter]
        [AutomaticRetry(Attempts = 0)]
        public void DeletePDF(string name, string email, string filename, string fileDirectory, string restoreDirectory, string pdfGUID)
        {
            // 原方法逻辑
        }

        [DeleteDrawingFailedFilter]
        [AutomaticRetry(Attempts = 0)]
        public void UpdateXARecord(string name, string email, string filename, string dbLibrary, string sqlGUID)
        {
            // 原方法逻辑
        }
    }
}

额外优化建议

  1. 项目依赖调整:如果过滤器在Web项目,Core项目需要引用Web项目;更合理的做法是把过滤器移到Core项目(因为Job逻辑在Core),避免依赖倒置。
  2. 过滤器依赖注入:原过滤器中直接new EmailJob()和BackgroundJobClient的写法不推荐,建议通过构造函数注入依赖:
public class DeleteDrawingFailedFilterAttribute : JobFilterAttribute, IApplyStateFilter
{
    private readonly IBackgroundJobClient _backgroundJobClient;
    private readonly IEmailJob _emailJob;

    // 构造函数注入依赖
    public DeleteDrawingFailedFilterAttribute(IBackgroundJobClient backgroundJobClient, IEmailJob emailJob)
    {
        _backgroundJobClient = backgroundJobClient;
        _emailJob = emailJob;
    }

    public void OnStateApplied(ApplyStateContext context, IWriteOnlyTransaction transaction)
    {
        FailedState failedState = context.NewState as FailedState;
        if (failedState == null) return;

        // 原逻辑,替换new的对象为注入的实例
        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)
    {
        // 留空或实现必要逻辑,不要抛出NotImplementedException
    }
}

同时在Program.cs中注册过滤器为服务:

builder.Services.AddTransient<DeleteDrawingFailedFilterAttribute>();

内容的提问来源于stack exchange,提问作者Correllion115

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.23 04:27:16