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
Related
I was wondering if Quartz.Net has a way to tell (or better write in the logs) on the next execution... I mean, I have got a job that runs at 10:00 AM and it's scheduled to run every two hours... is there a way I can write something as Next run on 12:00AM? or do I have to parse the cron expression, then add it to the current date?
Thanks in advance
Not sure what logger you are using, but this approach should work with any logger that you can create instances of. Create instance of logger and pass it to job via JobDataMap and then use it inside of job. IJobExecutionContext.NextFireTimeUtc will tell you next execution time which you can write to a logger
using System;
using System.Threading.Tasks;
using NLog;
using Quartz;
using Quartz.Impl;
namespace QuartzSampleApp
{
public class Program
{
private static async Task Main(string[] args)
{
var logger = LogManager.GetCurrentClassLogger();
StdSchedulerFactory factory = new StdSchedulerFactory();
IScheduler scheduler = await factory.GetScheduler();
await scheduler.Start();
IJobDetail job = JobBuilder.Create<HelloJob>()
.WithIdentity("job1", "group1")
.Build();
job.JobDataMap["logger"] = logger; // add logger to job data map
ITrigger trigger = TriggerBuilder.Create()
.WithIdentity("trigger1", "group1")
.StartNow()
.WithSimpleSchedule(x => x
.WithIntervalInSeconds(10)
.RepeatForever())
.Build();
await scheduler.ScheduleJob(job, trigger);
await Task.Delay(TimeSpan.FromSeconds(60));
await scheduler.Shutdown();
Console.WriteLine("Press any key to close the application");
Console.ReadKey();
}
// simple log provider to get something to the console
}
public class HelloJob : IJob
{
public async Task Execute(IJobExecutionContext context)
{
await Console.Out.WriteLineAsync("Greetings from HelloJob!");
var logger = context.JobDetail.JobDataMap["logger"] as ILogger;
logger.Log(LogLevel.Info, "Next job execution at " + context.NextFireTimeUtc);
}
}
}
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 using Quartz.Net to implement some asynchronous processing within the IIS worker process (IIS 8.5). One particular job may take more 10 minutes to run and does a lot of processing.
The following pieces of code illustrate how I am handling job life cycle.
Schedule definition
public class JobScheduler
{
private static IScheduler _quartzScheduler;
public static void Start()
{
_quartzScheduler = new StdSchedulerFactory().GetScheduler();
_quartzScheduler.JobFactory = new NinjectJobFactory();
ScheduleTheJob(_quartzScheduler);
_quartzScheduler.Context.Add("key", "scheduler");
_quartzScheduler.Start();
}
private static void ScheduleTheJob(IScheduler scheduler)
{
IJobDetail job = JobBuilder.Create<JobClass>().UsingJobData("JobKey", "JobValue").Build();
ITrigger trigger = TriggerBuilder.Create()
.WithIdentity("JobTrigger", "JobGroup").UsingJobData("JobKey", "JobTrigger").StartNow()
.WithSimpleSchedule(x => x
// 30 minutes for refresh period
.WithIntervalInSeconds(ConfigService.JobRefreshPeriod)
.RepeatForever())
.Build();
scheduler.ScheduleJob(job, trigger);
}
public static void StopScheduler(bool waitForCompletion)
{
_quartzScheduler.Shutdown(waitForCompletion);
}
}
Application pool shutdown handling
public class ApplicationPoolService : IApplicationPoolService
{
public bool IsShuttingDown()
{
return System.Web.Hosting.HostingEnvironment.ShutdownReason != ApplicationShutdownReason.None;
}
public ApplicationShutdownReason GetShutdownReason()
{
return System.Web.Hosting.HostingEnvironment.ShutdownReason;
}
}
public class HostingEnvironmentRegisteredObject : IRegisteredObject
{
public void Stop(bool immediate)
{
if (immediate)
return;
JobScheduler.StopScheduler(waitForCompletion: true);
var logger = NinjectWebCommon.Kernel.Get<ILoggingService>();
var appPoolService = NinjectWebCommon.Kernel.Get<IApplicationPoolService>();
var reason = appPoolService.GetShutdownReason().ToString();
logger.Log(LogLevel.Info, $"HostingEnvironmentRegisteredObject.stop called with shutdown reason {reason}");
}
}
Global.asax.cs wiring up
protected void Application_Start()
{
JobScheduler.Start();
HostingEnvironment.RegisterObject(new HostingEnvironmentRegisteredObject());
}
protected void Application_Error()
{
Exception exception = Server.GetLastError();
Logger.Log(LogLevel.Fatal, exception, "Application global error");
}
protected void Application_End(object sender, EventArgs e)
{
// stopping is now triggered in HostingEnvironmentRegisteredObject
// JobScheduler.StopScheduler(false);
// get shutdown reason
var appPoolService = NinjectWebCommon.Kernel.Get<IApplicationPoolService>();
var reason = appPoolService.GetShutdownReason().ToString();
Logger.Log(LogLevel.Info, $"Application_End called with shutdown reason {reason}");
}
Job step description
if (ApplicationPoolService.IsShuttingDown())
{
Logger.Log(LogLevel.Info, "(RefreshEnvironmentImportingSystemData) Application pool is shutting down");
return;
}
// about 20-30 steps may be here
environments.ForEach(env =>
{
if (ApplicationPoolService.IsShuttingDown())
return;
// do heavy processing for about 2 minutes (worst case), typically some 10-20s
}
// one job step may allocate several hundreds of MB, so GC is called to reclaim some memory sooner
// it takes a few seconds (worst case)
GC.Collect();
GC.WaitForPendingFinalizers();
GC.WaitForFullGCComplete();
GC.Collect();
Initially, I was stopping the scheduler when Application_End was called, but I realized that was called when application pool was about to be killed, so I move it when application pool was notified that its shutdown has been started.
I have left application pool with its default value for Shutdown time limit (90 seconds).
Job is configured to not allow concurrent executions.
I want to achieve the following:
avoid forced killing of job during actual execution
minimize the time when two worker processes run in the same time (shutting down one in parallel with the one just started to handle new requests)
Question: did I manage correctly the scheduled jobs or can I make improvements?
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Quartz;
using Quartz.Impl;
using Quartz.Job;
using ConsoleApplication2;
namespace Lesson1
{
class Program
{
static void Main(string[] args)
{
//Create the scheduler factory
ISchedulerFactory schedulerFactory = new StdSchedulerFactory();
//Ask the scheduler factory for a scheduler
IScheduler scheduler = schedulerFactory.GetScheduler();
//Start the scheduler so that it can start executing jobs
scheduler.Start();
// Create a job of Type WriteToConsoleJob
IJobDetail job = JobBuilder.Create(typeof(WriteToConsoleJob)).Build();
ITrigger trigger = TriggerBuilder.Create().WithDailyTimeIntervalSchedule(s => s.WithIntervalInMinutes(15).OnMondayThroughFriday().StartingDailyAt(TimeOfDay.HourAndMinuteOfDay(9, 0))).Build();
scheduler.ScheduleJob(job, trigger);
//A nice way to stop the scheduler, waiting for jobs that are running to finish
scheduler.Shutdown(true);
}
}
}
I have created a test job and its working fine for weekdays repeating after 15 minutes starting at 0900 hours but i want to run it for specific interval of time i.e. 0900 to 1500 hours.
And i don't want to use CronTrigger for this.
Add an EndingDailyAt call:
ITrigger trigger = TriggerBuilder
.Create()
.WithDailyTimeIntervalSchedule(s =>
s.WithIntervalInMinutes(15)
.OnMondayThroughFriday()
.StartingDailyAt(TimeOfDay.HourAndMinuteOfDay(9, 0))
.EndingDailyAt(TimeOfDay.HourAndMinuteOfDay(15, 0)))
.Build();
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.