netcore Quartz 注入 DbContextFactory 单例实现调度任务使用数据库

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(); 单独创建数据库访问上下文单例,而非构造注入。

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值