I tried to make dependency injection into my Quartz. I need to fire scheduler with my program after clicking a button on my web interface. Generally I had:
this.job = JobBuilder.Create<Program>()
.WithIdentity("myJob", "group1")
.Build();
But this above just fire Program with default constructor (Program()).
Unfortanelly I have a program, that need constructor with parameters:
public class Program : IJob, IProgram
{
private readonly IDataBaseService _databaseservice;
public Program(IDataBaseService databaseservice)
{
_databaseservice = databaseservice;
}
public async Task Execute(IJobExecutionContext context)
{
await _databaseservice.DoSmth();
}
So I need to call Program(IDataBaseService databaseservice)
I've read many guides and tutorials and examples and still I can't find an answer. Most of answers are about Quartz starting at a start of application, but I want it to start with depndency injection after I use button on web interface.
Here is my code:
Startup.cs:
public void ConfigureServices(IServiceCollection services)
{
services.AddTransient<IDataBaseService, DB2Service>();
services.AddTransient<IJobFactory, MyJobFactory>();
services.AddSingleton<IMyScheduler, MyScheduler>();
services.AddSingleton<IProgram, Program>();
services.AddControllersWithViews();
}
MVC Controler SiteController.cs:
public class SiteController : Controller
{
private readonly IMyScheduler _MyScheduler;
private readonly IDataBaseService _DataBaseService;
private readonly IProgram _Program;
public SiteController(IMyScheduler MyScheduler, IDataBaseService DataBaseService, IProgram Program)
{
_MyScheduler = MyScheduler;
_DataBaseService = DataBaseService;
_Program = Program;
}
//this action is called after button click on web interface
public async Task<ActionResult> ButtonClick()
{
//This is where i want to start Quartz. I don't want it to start at start of app.
//I want it to start when I click button on web interface.
await MyScheduler.Run_A_Scheduler();
return OK();
}
}
MyJobFactory.cs:
public class MyJobFactory : IJobFactory
{
private readonly IServiceProvider _provider;
public MyJobFactory(IServiceProvider provider)
{
_provider = provider;
}
public IJob NewJob(TriggerFiredBundle bundle, IScheduler scheduler)
{
try
{
return (IJob)this._provider.GetService(bundle.JobDetail.JobType);
}
catch (Exception e)
{
throw new SchedulerException(string.Format("Problem while instantiating job '{0}' from the ASPnet Core IOC", bundle.JobDetail.Key), e);
}
}
public void ReturnJob(IJob job)
{
var disposable = job as IDisposable;
disposable?.Dispose();
}
}
MyScheduler.cs:
public class MyScheduler: IMyScheduler
{
IScheduler scheduler;
IJobDetail job;
ITrigger trigger;
public MyScheduler(IJobFactory myJobFactory)
{
scheduler = new StdSchedulerFactory()
.GetScheduler()
.Result;
scheduler.JobFactory = myJobFactory;
}
public async Task Run_A_Scheduler()//this is a function that I am calling from controler
{
this.job = CreateJob<Program>() //definition at the end
this.trigger = TriggerBuilder.Create()
.StartNow()
.WithSimpleSchedule(x => x
.WithIntervalInSeconds(20) //example with 20 seconds, normaly every other hour
.RepeatForever())
.Build();
await this.scheduler.ScheduleJob(job, this.trigger);
await this.scheduler.Start();
}
private IJobDetail CreateJob<T>() where T : IJob
{
return JobBuilder.Create<T>()
.WithIdentity(typeof(T).Name)
.Build();
}
}
Program.cs
public class Program : IJob, IProgram
{
private readonly IDataBaseService _databaseservice;
public Program(IDataBaseService databaseservice)
{
_databaseservice = databaseservice;
}
public async Task Execute(IJobExecutionContext context)
{
await _databaseservice.DoSmth();
}
Interfaces IDataBaseService, IMyScheduler, IProgram are mostly empty and just here for dependency injection sake. IJobFactory is from Quartz library.
Please help. I tried many solutions, and still I don't get it. I want to fire with Quartz schedule my Program.cs, which have constructor with parameters. I don't know, how to invoke Program.cs with quartz schedule when I click button on web interface.
This is what I have done to make it work.
in appsettings.json there is a control variable:
"Generate": true
Then in Startup.cs:
//adding quartz
services.AddSingleton<IJobFactory, SingletonJobFactory>();
services.AddSingleton<ISchedulerFactory, StdSchedulerFactory>();
services.AddTransient<AutoTaskGeneration>();
services.AddSingleton<QuartzJobTaskGeneration>();
services.AddHostedService<QuartzHostedService>();
SingletonJobFactory:
public class SingletonJobFactory : IJobFactory
{
private readonly IServiceProvider _serviceProvider;
public SingletonJobFactory(IServiceProvider serviceProvider)
{
_serviceProvider = serviceProvider;
}
public IJob NewJob(TriggerFiredBundle bundle, IScheduler scheduler)
{
return _serviceProvider.GetRequiredService(bundle.JobDetail.JobType) as IJob;
}
public void ReturnJob(IJob job) { }
}
AutoTaskGeneration:
public class AutoTaskGeneration
{
private readonly ITaskGeneration _taskGeneration;
private readonly IConfiguration _nativeConfiguration;
public AutoTaskGeneration(ITaskGeneration taskGeneration, IConfiguration configuration)
{
_taskGeneration = taskGeneration; //class that is needed to be run
_nativeConfiguration = configuration;
}
public async Task Run()
{
bool Generate = _nativeConfiguration.GetValue<bool>("Generate");
if (Generate)
{
await _taskGeneration.GenerateTasksRUN(); //function, that i want to run
}
}
}
QuartzJobTaskGeneration
[DisallowConcurrentExecution]
public class QuartzJobTaskGeneration : IJob
{
private readonly IServiceProvider _provider;
public QuartzJobTaskGeneration(IServiceProvider provider)
{
_provider = provider;
}
public async Task Execute(IJobExecutionContext context)
{
using (var scope = _provider.CreateScope())
{
// Resolve the Scoped service
var service = scope.ServiceProvider.GetService<AutoTaskGeneration>();
await service.Run();
}
}
}
QuartzHostedService
public class QuartzHostedService : IHostedService
{
private readonly ISchedulerFactory _schedulerFactory;
private readonly IJobFactory _jobFactory;
private readonly QuartzJobTaskGeneration _quartzJobTaskGeneration;
public QuartzHostedService(
ISchedulerFactory schedulerFactory,
IJobFactory jobFactory,
QuartzJobTaskGeneration quartzJobTaskGeneration,
)
{
_schedulerFactory = schedulerFactory;
_jobFactory = jobFactory;
_quartzJobTaskGeneration = quartzJobTaskGeneration;
}
public IScheduler Scheduler { get; set; }
public async Task StartAsync(CancellationToken cancellationToken)
{
Scheduler = await _schedulerFactory.GetScheduler(cancellationToken);
Scheduler.JobFactory = _jobFactory;
await Scheduler.UnscheduleJob(new TriggerKey("QuartzJobTaskGeneration.trigger", "QuartzJobTaskGenerationGroup"));
var job = JobBuilder
.Create<QuartzJobTaskGeneration>()
.WithIdentity("QuartzJobTaskGeneration", "QuartzJobTaskGenerationGroup")
.WithDescription("QuartzJobTaskGeneration")
.Build();
var trigger = TriggerBuilder
.Create()
.WithIdentity($"QuartzJobTaskGeneration.trigger", "QuartzJobTaskGenerationGroup")
.WithCronSchedule("0 30 7 ? * MON-SAT *")
.WithDescription("QuartzJobTaskGeneration.trigger")
.Build();
}
}
And now with all of this I can control when program should fire based on his cron and Generate value in appsetings