Quartz 注入 DbConext 如果直接在Service 中配置
builder.Services.AddDbContext(options => options.UseSqlServer(builder.Configuration.GetConnectionString(“DefaultConnection”)));
builder.Services.AddSingleton<IJobFactory,QuartzJobFactory>();
builder.Services.AddSingleton();
builder.Services.AddHostedService<Quartz.QuartzHostedService>();
builder.Services.AddQuartzHostedService(q => q.WaitForJobsToComplete = true);
此时编译不通过,因为 AddDbContext 是作用域周期,和Quartz单例周期冲突,改为使用 AddDbContextFactory
builder.Services.AddDbContextFactory(options => options.UseSqlServer(builder.Configuration.GetConnectionString(“DefaultConnection”)));
builder.Services.AddSingleton<IJobFactory,QuartzJobFactory>();
builder.Services.AddSingleton();
builder.Services.AddHostedService<Quartz.QuartzHostedService>();
builder.Services.AddQuartzHostedService(q => q.WaitForJobsToComplete = true);
此时作用域冲突的问题就解决了,Quartz 工厂模式和任务类以及数据库访问都是单例,但是接下来如果在任务类里面注入
IDbContextFactory _contextFactory;
那么问题就来了,早期版本Quartz不支持构造函数依赖注入,3.3以上版本已经主动支持,不需要在 Service 里面注册。
但是需要通过Quartz的工厂模式进行实例构造传递才可以,定义Quartz工厂模式类,实现对象解析注入传递。
namespace GzAIManager.Quartz
{
public class QuartzJobFactory : IJobFactory
{
private readonly IServiceProvider _serviceProvider;
public QuartzJobFactory(IServiceProvider serviceProvider)
{
_serviceProvider = serviceProvider;
}
public IJob NewJob(TriggerFiredBundle bundle, IScheduler scheduler)
{
var jobType = bundle.JobDetail.JobType;
var job = _serviceProvider.GetRequiredService(jobType) as IJob;
if (job == null)
{
throw new SchedulerException($"无法从服务容器中解析Job类型: {jobType.FullName}");
}
return job;
}
public void ReturnJob(IJob job)
{
var disposable = job as IDisposable;
disposable?.Dispose();
}
}
}
然后在任务类进行构造函数传递参数,此时就可以正常在ijob继承任务类中使用构造函数注入。
namespace GzAIManager.Quartz
{
public class KnowTableJob : IJob
{
private readonly IDbContextFactory<BaseDbContext> _contextFactory;
public KnowTableJob(IDbContextFactory<BaseDbContext> contextFactory)
{
_contextFactory = contextFactory;
}
public async Task Execute(IJobExecutionContext context)
{
try
{
await Task.Delay(1000);
var jobData = context.MergedJobDataMap;
var message = jobData.GetString("Message") ?? "默认消息";
var testdb = _contextFactory.CreateDbContext();
var count = await testdb.Admin.CountAsync();
Console.WriteLine(count.ToString());
}
catch (Exception ex)
{
throw new JobExecutionException(ex);
}
}
}
}
任务调度类定义
namespace GzAIManager.Quartz
{
public class QuartzHostedService : IHostedService
{
private readonly ISchedulerFactory _schedulerFactory;
private readonly IServiceProvider _serviceProvider;
private IScheduler? _scheduler;
public QuartzHostedService(ISchedulerFactory schedulerFactory, IServiceProvider serviceProvider)
{
_schedulerFactory = schedulerFactory;
_serviceProvider = serviceProvider;
}
public async Task StartAsync(CancellationToken cancellationToken)
{
_scheduler = await _schedulerFactory.GetScheduler();
var JobFactory = _serviceProvider.GetService<IJobFactory>();
if (JobFactory != null)
{
_scheduler.JobFactory = JobFactory;
}
await _scheduler.Start(cancellationToken);
await ConfigureJobs(_scheduler);
}
public async Task StopAsync(CancellationToken cancellationToken)
{
if (_scheduler != null)
{
await _scheduler.Shutdown(cancellationToken);
}
}
private async Task ConfigureJobs(IScheduler scheduler)
{
var demoJob = JobBuilder.Create<KnowTableJob>().WithIdentity("demoJob", "group1").UsingJobData("Message", "这是来自JobDataMap的消息").Build();
var demoTrigger = TriggerBuilder.Create().WithIdentity("demoTrigger", "group1").StartNow().WithSimpleSchedule(x => x.WithIntervalInSeconds(20).RepeatForever()).Build();
await scheduler.ScheduleJob(demoJob, demoTrigger);
}
}
}
这样就基本解决了,Quartz在任务具体实现类中通过构造函数注入工厂模式的 dbcontextfactory , 然后操作数据库。
但是又出现一个问题
builder.Services.AddDbContext 作用域周期数据库上下文
builder.Services.AddDbContextFactory 单例周期数据库上下文
我们在上面的例子中使用了builder.Services.AddDbContextFactory,注入了数据库访问上下文,那么项目就变为数据库单例访问模式,此时你如果再添加 builder.Services.AddDbContext ,作用域访问也配置进去,那显然是有问题,因为两个数据库上下文,在高并发场景下出现周期冲突,调用不明确等问题,任何时候全局的数据库配置都应该只有1个,如果需要特殊的配置,那么就应该在单位内部配置。
所以,解决方案是,如果必须使用builder.Services.AddDbContext 作用域周期数据库上下文来实现项目,那么使用Quartz过程中,就应该在任务实现类中使用_contextFactory.CreateDbContext(); 单独创建数据库访问上下文单例,而非构造注入。
1324

被折叠的 条评论
为什么被折叠?



