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

ABP Quartz动态作业中UnitOfWorkAttribute失效问题求助

Quartz作业中EF Core DbContext释放问题的解决办法

调整自定义JobFactory的服务范围管理

Quartz默认把Job当成单例实例,但DbContext是Scoped生命周期,生命周期不匹配就会导致DbContext被提前释放。你需要在自定义JobFactory里为每个Job创建独立的服务范围,执行完成后再释放:

public class CustomJobFactory : IJobFactory
{
    private readonly IServiceScopeFactory _scopeFactory;

    public CustomJobFactory(IServiceScopeFactory scopeFactory)
    {
        _scopeFactory = scopeFactory;
    }

    public IJob NewJob(TriggerFiredBundle bundle, IScheduler scheduler)
    {
        var scope = _scopeFactory.CreateScope();
        try
        {
            var job = scope.ServiceProvider.GetRequiredService(bundle.JobDetail.JobType) as IJob;
            // 将服务范围存入JobDataMap,方便后续释放
            bundle.JobDetail.JobDataMap["ServiceScope"] = scope;
            return job;
        }
        catch
        {
            scope.Dispose();
            throw;
        }
    }

    public void ReturnJob(IJob job)
    {
        // 从JobDataMap取出服务范围并释放
        if (job is IJobWithDataMap jobWithData && 
            jobWithData.JobDataMap.TryGetValue("ServiceScope", out var scopeObj) && 
            scopeObj is IServiceScope scope)
        {
            scope.Dispose();
        }
        // 处理Job自身的资源释放
        else if (job is IAsyncDisposable asyncDisposable)
        {
            _ = asyncDisposable.DisposeAsync();
        }
        else if (job is IDisposable disposable)
        {
            disposable.Dispose();
        }
    }
}

需要让你的Job实现IJobWithDataMap接口来获取JobDataMap,或者在Job基类中封装该逻辑,确保能拿到存储的服务范围。

用基类统一管理工作单元

ABP的UnitOfWorkAttribute对Quartz Job无效,因为Job不在ABP的请求拦截管道内。可以编写一个Job基类,封装工作单元逻辑,替代手动调用UnitOfWorkManager.Begin:

public abstract class UnitOfWorkQuartzJob : IJob
{
    private readonly IUnitOfWorkManager _unitOfWorkManager;

    protected UnitOfWorkQuartzJob(IUnitOfWorkManager unitOfWorkManager)
    {
        _unitOfWorkManager = unitOfWorkManager;
    }

    public async Task Execute(IJobExecutionContext context)
    {
        using (var uow = _unitOfWorkManager.Begin())
        {
            await RunJobAsync(context);
            await uow.CompleteAsync();
        }
    }

    // 子类仅需实现此方法编写业务逻辑
    protected abstract Task RunJobAsync(IJobExecutionContext context);
}

后续所有Quartz Job都继承这个基类,业务代码写在RunJobAsync中,即可自动套用ABP的工作单元配置,无需每次手动开启UOW。

确保Job注册为Scoped生命周期

在Quartz模块配置中,将Job注册为Scoped,配合上述JobFactory使用:

public override void ConfigureServices(ServiceConfigurationContext context)
{
    context.Services.AddQuartz(q =>
    {
        q.UseMicrosoftDependencyInjectionJobFactory<CustomJobFactory>();
        // 注册Job并指定持久化存储
        q.AddJob<YourBizJob>(opts => opts.WithIdentity("YourBizJob").StoreDurably());
    });

    // 关键:将Job注册为Scoped
    context.Services.AddScoped<YourBizJob>();
}

这样每次创建Job实例时,都会在新的服务范围内获取,DbContext也会是全新实例,不会被提前释放。

检查DbContext的注册配置

确认你的DbContext在ABP模块中为默认的Scoped注册,不要改为Singleton:

public override void ConfigureServices(ServiceConfigurationContext context)
{
    context.Services.AddAbpDbContext<YourDbContext>(options =>
    {
        options.AddDefaultRepositories();
    });
}

ABP默认会将DbContext注册为Scoped,除非手动修改生命周期,这一步需避免配置错误。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.25 07:31:23