I have a custom windows service, and I'd like to use Quartz .NET to schedule when the service runs. Now, I understand the basics of quartz.NET, but I'm not sure how I would hook it up to a windows service.. So, lets say i have Service.exe which I want to run every hour. How would I implement this functionality via Quartz? I know this is kind of a vague question, but there's really no other way to ask it.
Thanks in advance.
You need to setup a job and a trigger. The job is called by a trigger.(http://quartznet.sourceforge.net/tutorial/lesson_3.html). Here's an example running every hour.
// construct a scheduler factory
ISchedulerFactory schedFact = new StdSchedulerFactory();
// get a scheduler
IScheduler sched = schedFact.GetScheduler();
sched.Start();
// construct job info
JobDetail jobDetail = new JobDetail("myJob", null, typeof(DumbJob));
// fire every hour
Trigger trigger = TriggerUtils.MakeHourlyTrigger();
// start on the next even hour
trigger.StartTime = TriggerUtils.GetEvenHourDate(DateTime.UtcNow);
trigger.Name = "myTrigger";
sched.ScheduleJob(jobDetail, trigger);
Here is your class which calls Service.exe.
public class DumbJob : IJob
{
public void Execute(JobExecutionContext context)
{
string instName = context.JobDetail.Name;
string instGroup = context.JobDetail.Group;
// Note the difference from the previous example
JobDataMap dataMap = context.MergedJobDataMap;
string jobSays = dataMap.GetString("jobSays");
float myFloatValue = dataMap.GetFloat("myFloatValue");
ArrayList state = (ArrayList) dataMap.Get("myStateData");
state.Add(DateTime.UtcNow);
Console.WriteLine("Instance {0} of DumbJob says: {1}", instName, jobSays);
}
}
You could also just start a thread in a windows service, keep track of when you last fired the exe and then reset afterwards. It's a bit simpler thatn Quartz, and would accomplish the same things. However, your question was Quartz specific.
Related
I have a ASP.NET Core 3.1 Web API and have chosen to use Quartz.net to execute recurring tasks.
I've implemented the QuartzHostedService as recommended and I manage to start job successfully by declaring them in the startup.
But I also need to add and execute some jobs dynamically by getting them in a database, and I don't want to deploy and restart my Web API each time I need to add a new recurring job (because some of my clients could add their own recurring tasks).
I managed to add jobs in the quartzHosted service by implemented a method in it:
public async Task AddJob(JobSchedule job)
{
Scheduler = await _schedulerFactory.GetScheduler();
Scheduler.JobFactory = _jobFactory;
var j = CreateJob(job);
var trigger = CreateTrigger(job);
await Scheduler.ScheduleJob(j, trigger);
job.JobStatus = JobStatus.Scheduling;
await Scheduler.Start();
job.JobStatus = JobStatus.Running;
}
I have implemented an InitialJob that have the goal to ask the database for new jobs (this job is declared in the startup).
In this InitialJob, my execute method is like this:
// Create a new scope
using (var scope = _provider.CreateScope())
{
// Resolve the Scoped service
var _uow = scope.ServiceProvider.GetService<IUnitOfWork>();
var stats = await _uow.Stat.GetActive();
var test = await _quartzHostedService.GetAllJobs();
foreach (var clientStat in stats.GroupBy(s => s.Clients))
{
foreach (var job in clientStat)
{
var key = new JobKey($"{job.Constant}.{clientStat.Key.FirstOrDefault().Id}");
var jobExist = _quartzHostedService.Scheduler.CheckExists(key);
if (!jobExist.Result)
{
var jobSchedule = new JobSchedule(jobType: typeof(GenericSimulationJob), cronExpression: job.Trigger.CronTask, job, clientStat.Key.FirstOrDefault().Id);
await _quartzHostedService.AddJob(jobSchedule);
}
}
}
var test2 = await _quartzHostedService.GetAllJobs();
}
When I check the jobs before adding them (variable test) I only have 2 jobs in the quartzHostedService, after adding them in the foreach (variable test2) I have 5. Perfect.
But it seems I'm not able to execute them. I only see 2 jobs in my QuartzHostedService.Schedule.CurrentlyExecutedJob.
I also see only 2 jobs in my jobFactory variable.
I tried to sleep and restart my Schedule but nothing works. Did I missed something? Is it just possible?
Thanks for your help
I found my mistake. I forgot to declare my IJob (which contains my scheduleJobs) in the startup, even if there is no job schedule in it at start.
services.AddSingleton<GenericSimulationJob>();
Then my code works (without start scheduler instance again).
I have the following code:
public class JobScheduler
{
public static void Start()
{
IScheduler scheduler = StdSchedulerFactory.GetDefaultScheduler();
scheduler.Start();
//JOB - Automatically renew member subscriptions
IJobDetail subscriptionsJob = JobBuilder.Create<UpdateSubscriptions>().Build();
ITrigger subscriptionsTrigger = TriggerBuilder.Create()
.WithIdentity("updateSubscriptions", "updateGroup")
.StartNow()
.WithSimpleSchedule(x => x
.WithIntervalInHours(24)
.RepeatForever()
)
.Build();
scheduler.ScheduleJob(subscriptionsJob, subscriptionsTrigger);
}
}
Now, whenever this is run it seems that two threads are actually executing this creating unforeseen issues where the code is executed twice.
I am not sure exactly why this is happening? Can anyone see what i might have done wrong?
It is worth mentioning that this is an umbraco application
Update
Please note that my job has the DisallowConcurrentExecution attribute:
[DisallowConcurrentExecution]
public class UpdateSubscriptions : IJob
{
Update The log
So ive checked out my log and as expected two worker threads are working on the file:
P19484/D2/TDefaultQuartzScheduler_Worker-3
P166696/D2/TDefaultQuartzScheduler_Worker-1
I have a C# service and I need to run a function once a week.
I have a working C# service which currently is running on a timer every 60 seconds.
Please see below a section of the services OnStart function:
// Set up a timer to trigger.
System.Timers.Timer timer = new System.Timers.Timer
{
Interval = 60000 //*1000; // 60 second
};
timer.Elapsed += delegate {
// Runs the code every 60 seconds but only triggers it if the schedule matches
Function1();
};
timer.Start();
The above code calls Function1() every 60 seconds and I am checking it in Function1 if the current dayofweek and time matches the schedule and if it does than execute the rest of the function.
Although this does work it not the most elegant way IMO.
I have tried using Quartz.net as it was looking promising but when I used all the examples available online (questions answered some 7 years ago in 2012), it is showing as an error in visual studio:
using System;
using Quartz;
public class SimpleJob : IJob
{
public void Execute(IJobExecutionContext context)
{
throw new NotImplementedException();
}
}
This is erroring
(Error CS0738 'SimpleJob' does not implement interface member 'IJob.Execute(IJobExecutionContext)'. 'SimpleJob.Execute(IJobExecutionContext)' cannot implement 'IJob.Execute(IJobExecutionContext)' because it does not have the matching return type of 'Task'.)
but this does not:
public Task Execute(IJobExecutionContext context)
{
throw new NotImplementedException();
}
Could someone give a current working example of a job scheduled through Quartz.net for a beginner?
Or using another elegant method than Quartz.net in a C# service?
First of all we need to implement a job implementation. For example:
internal class TestJob : IJob
{
public Task Execute(IJobExecutionContext context)
{
Console.WriteLine("Job started");
return Task.CompletedTask;
}
}
Now we need to write a method which will return a Scheduler of Quartz :
static async Task TestScheduler()
{
// construct a scheduler factory
NameValueCollection props = new NameValueCollection
{
{ "quartz.serializer.type", "binary" }
};
StdSchedulerFactory factory = new StdSchedulerFactory(props);
// get a scheduler
IScheduler sched = await factory.GetScheduler();
await sched.Start();
// define the job and tie it to our HelloJob class
IJobDetail job = JobBuilder.Create<TestJob>()
.WithIdentity("myJob", "group1")
.Build();
// Trigger the job to run now, and then every 40 seconds
ITrigger trigger = TriggerBuilder.Create()
.WithIdentity("myTrigger", "group1")
.StartNow()
.WithSimpleSchedule(x => x
.WithIntervalInMinutes(1)
.RepeatForever())
.Build();
await sched.ScheduleJob(job, trigger);
}
and in the Main method of the Program we will need to write following code:
static async Task Main()
{
Console.WriteLine("Test Scheduler started");
await TestScheduler();
Console.ReadKey();
}
Now this will keep executing after every minute.
Hope it helps.
I am currently investigating using Quartz.NET for scheduling tasks in my system. As an example of how I am using Quartz.NET, below is a very simple example demonstrating how I am scheduling a task:
class Program
{
static void Main(string[] args)
{
var properties = new NameValueCollection();
properties["quartz.scheduler.instanceName"] = "TestScheduler";
properties["quartz.scheduler.instanceId"] = "instance_one";
properties["quartz.jobStore.type"] = "Quartz.Impl.AdoJobStore.JobStoreTX, Quartz";
properties["quartz.jobStore.useProperties"] = "true";
properties["quartz.jobStore.dataSource"] = "default";
properties["quartz.jobStore.tablePrefix"] = "QRTZ_";
properties["quartz.jobStore.lockHandler.type"] = "Quartz.Impl.AdoJobStore.UpdateLockRowSemaphore, Quartz";
properties["quartz.dataSource.default.connectionString"] = "Server=.\\SqlExpress;Database=quartz;Trusted_Connection=True;";
properties["quartz.dataSource.default.provider"] = "SqlServer-20";
var scheduler = new StdSchedulerFactory(properties).GetScheduler();
scheduler.Start();
TriggerSimpleJob(scheduler);
Console.WriteLine("Waiting For Job");
Console.ReadLine();
}
private static void TriggerSimpleJob(IScheduler scheduler)
{
ITrigger trigger = TriggerBuilder.Create()
.WithIdentity("trigger1", "group1")
.StartAt(DateBuilder.EvenSecondDateAfterNow())
.UsingJobData("myTriggerParameter", "myTriggerValue")
.UsingJobData("myParameter", "triggerParameter")
.Build();
IJobDetail jobDetail = JobBuilder.Create<SimpleJob>().WithIdentity("job1", "group1")
.UsingJobData("myParameter", "myValue")
.Build();
scheduler.ScheduleJob(jobDetail, trigger);
}
}
public class SimpleJob : IJob
{
public void Execute(IJobExecutionContext context)
{
Console.WriteLine("Job completed");
}
}
The question I have is this:
I would like to decouple of the scheduling of jobs from the execution of jobs.
In the above example, after the job has been scheduled, if the process is still running when the scheduled time arrives the job is executing within this process. Ideally I would like to be able to have a dedicated server with an instance of the Quartz.NET scheduler running that is dedicated to executing jobs, and be able to schedule jobs from other processes knowing the job will be executed on this dedicated server.
I have tried simply setting the property "quartz.threadPool.threadCount" to "0" on the process that schedules jobs, but this throws an exception. Is there any configuration properties on the scheduler that will achieve what I am trying to do?
Good morning,
you can read my answer here.
What I would suggest is to use ADO.NET Job Store (and it seems you're using it).
The application in charge of scheduling jobs should be configured setting the property threadPool to ZeroSizeThreadPool:
properties["quartz.threadPool.type"] = "Quartz.Simpl.ZeroSizeThreadPool, Quartz";
You can read more about this type of thread-pool here.
the application in charge of the execution of the jobs should be configured with these settings:
properties["quartz.threadPool.type"] = "Quartz.Simpl.SimpleThreadPool, Quartz";
properties["quartz.threadPool.threadCount"] = "10";
properties["quartz.threadPool.threadPriority"] = "Normal";
Remove the line
scheduler.Start();
I've just added quartz.net dll to my bin and started my example. How do I call a C# method using quartz.net based on time?
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using Quartz;
using System.IO;
public partial class _Default : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
if(SendMail())
Response.write("Mail Sent Successfully");
}
public bool SendMail()
{
try
{
MailMessage mail = new MailMessage();
mail.To = "test#test.com";
mail.From = "sample#sample.com";
mail.Subject = "Hai Test Web Mail";
mail.BodyFormat = MailFormat.Html;
mail.Body = "Hai Test Web Service";
SmtpMail.SmtpServer = "smtp.gmail.com";
mail.Fields.Clear();
mail.Fields.Add("http://schemas.microsoft.com/cdo/configuration/smtpauthenticate", "1");
mail.Fields.Add("http://schemas.microsoft.com/cdo/configuration/sendusername", "redwolf#gmail.com");
mail.Fields.Add("http://schemas.microsoft.com/cdo/configuration/sendpassword", "************");
mail.Fields.Add("http://schemas.microsoft.com/cdo/configuration/smtpserverport", "465");
mail.Fields.Add("http://schemas.microsoft.com/cdo/configuration/smtpusessl", "true");
SmtpMail.Send(mail);
return (true);
}
catch (Exception err)
{
throw err;
}
}
}
Here I am just sending a mail on page load. How do I call SendMail() once in a day at a given time (say 6.00 AM) using quartz.net? I don't know how to get started. Should I configure it in my global.asax file? Any suggestion?
Did you try the quartz.net tutorial?
Since your web app might get recycled/restarted, you should probably (re-)intialize the quartz.net scheduler in the Application_Start handler in global.asax.cs.
Update (with complete example and some other considerations):
Here's a complete example how to do this using quartz.net. First of all, you have to create a class which implements the IJobinterface defined by quartz.net. This class is called by the quartz.net scheduler at tne configured time and should therefore contain your send mail functionality:
using Quartz;
public class SendMailJob : IJob
{
public void Execute(JobExecutionContext context)
{
SendMail();
}
private void SendMail()
{
// put your send mail logic here
}
}
Next you have to initialize the quartz.net scheduler to invoke your job once a day at 06:00. This can be done in Application_Start of global.asax:
using Quartz;
using Quartz.Impl;
public class Global : System.Web.HttpApplication
{
void Application_Start(object sender, EventArgs e)
{
ISchedulerFactory schedFact = new StdSchedulerFactory();
// get a scheduler
IScheduler sched = schedFact.GetScheduler();
sched.Start();
// construct job info
JobDetail jobDetail = new JobDetail("mySendMailJob", typeof(SendMailJob));
// fire every day at 06:00
Trigger trigger = TriggerUtils.MakeDailyTrigger(06, 00);
trigger.Name = "mySendMailTrigger";
// schedule the job for execution
sched.ScheduleJob(jobDetail, trigger);
}
...
}
That's it. Your job should be executed every day at 06:00. For testing, you can create a trigger which fires every minute (for example). Have a look at the method of TriggerUtils.
While the above solution might work for you, there is one thing you should consider: your web app will get recycled/stopped if there is no activity for some time (i.e. no active users). This means that your send mail function might not be executed (only if there was some activity around the time when the mail should be sent).
Therefore you should think about other solutions for your problem:
you might want to implement a windows service to send your emails (the windows service will always be running)
or much easier: implement your send mail functionality in a small console application, and set up a scheduled task in windows to invoke your console app once a day at the required time.
Add .Result at the end of schedFact.GetScheduler();
void Application_Start(object sender, EventArgs e)
{
ISchedulerFactory schedFact = new StdSchedulerFactory();
// get a scheduler
IScheduler sched = schedFact.GetScheduler().Result;
sched.Start();
// construct job info
JobDetail jobDetail = new JobDetail("mySendMailJob", typeof(SendMailJob));
// fire every`enter code here` day at 06:00
Trigger trigger = TriggerUtils.MakeDailyTrigger(06, 00);
trigger.Name = "mySendMailTrigger";
// schedule the job for execution
sched.ScheduleJob(jobDetail, trigger);
}
In addition to the good answer M4N provided, you can take a look at the spring.net integration of the quartz.net lib which allows to call methods without the need to implement IJob.
i searching for Quartz . i do this for my job:
1:instal Quartz from visual console:
PM> Install-Package quartz
2:create a class like this:
using Quartz;
public class Quartz : IJob
{
public void Execute(IJobExecutionContext context)
{
//do some
}
}
3.in global
using Quartz;
using Quartz.Impl;
protected void Application_Start(object sender, EventArgs e)
{
//for start time at first run after 1 hour
DateTimeOffset startTime = DateBuilder.FutureDate(1, IntervalUnit.Hour);
IJobDetail job = JobBuilder.Create<Quartz>()
.WithIdentity("job1")
.Build();
ITrigger trigger = TriggerBuilder.Create()
.WithIdentity("trigger1")
.StartAt(startTime)
.WithSimpleSchedule(x => x.WithIntervalInSeconds(10).WithRepeatCount(2))
.Build();
ISchedulerFactory sf = new StdSchedulerFactory();
IScheduler sc = sf.GetScheduler();
sc.ScheduleJob(job, trigger);
sc.Start();
}
it is code that doing some job in every 10second for 3time.
good luck