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
相关产品推荐
相关产品推荐

