Quite often I am asked to create a job to run within a windows service – the following demands applies:
- A Windows Service is needed since the demands for applying a schedule is not met by the “Scheduled Tasks” features from Windows. Due to that a Console App is ruled out.
- The job routine must be executed at a certain time – this must be configurable.
- When the job routine has been executed a schedule must be started – this must be configurable.
To accomplish this we will use:
- an app.config file to hold our configuration
- Two Timers – remember to use System.Threading.Timer rather than the System.Windows.Forms.Timer
What we need to do is:
- Create an OnStart event that starts the processing in a separate thread. This is always a good idea since you will avoid start problems if the process your are starting takes some time to finish.
- Calculate the time from now and until the job is supposed to run. Start a timer with exactly that delay by setting the duetime of the Timer.
- When the job executes… Create a new timer and set the Period of the Timer to correspond to the wanted schedule.
Here’s an example of one way to accomplish the task:
1 protected override void OnStart(string[] args)
2 {
3 //Start the actual job in a separate thread to avoid timeouts on start
4 backgroundWorker = new System.ComponentModel.BackgroundWorker();
5 backgroundWorker.WorkerReportsProgress = false;
6 backgroundWorker.DoWork += new DoWorkEventHandler(backgroundWorker_DoWork);
7 backgroundWorker.RunWorkerAsync();
8 }
9
10 void backgroundWorker_DoWork(object sender, DoWorkEventArgs e)
11 {
22 //the timer that initilizes that the job is executed for the first time at a certain time
12 StartInitTimer();
13 }
Let’s look at the StartInitTimer() method:
30 ///<summary>
31 /// Starts the initializing timer. this timer runs once and sets the aplicationlogic to run on the configured schedule
32 ///</summary>
33 private void StartInitTimer()
34 {
35 //calcuale the delay until the point of starting time
36 DateTime start = DateTime.Parse(ConfigurationManager.AppSettings[“ImportStartTime”]);
37 DateTime current = DateTime.Now;
38 TimeSpan timeBeforeStart;
39 if (start < current)
40 {
41 //the starttime for today has passed – calculate the start time after midnight
42 DateTime midnight = GetMidnight();
43 TimeSpan timeBeforeMidnight = midnight.Subtract(current);
44 timeBeforeStart = timeBeforeMidnight.Add(new TimeSpan(start.Hour, start.Minute, start.Second));
45 }
46 else
47 {
48 timeBeforeStart = start.Subtract(current);
49 }
50 //start the timer
51 initTimer = new System.Threading.Timer(InitTimer_Elapsed, null, timeBeforeStart, new TimeSpan(0, 0, 0)); //setting timespan to -1 milliseconds causes the timer to run once
52 }
53
54 ///<summary>
55 /// Helper method – Calculates a datetime object for the next coming midnight
56 ///</summary>
57 ///<returns></returns>
58 private DateTime GetMidnight()
59 {
60 DateTime midnight = new DateTime(DateTime.Now.Year, DateTime.Now.Month, DateTime.Now.Day, 0, 0, 0);
61 midnight = midnight.AddDays(1);
62 return midnight;
63 }
64
65 ///<summary>
66 /// handles the elapsed event of the imitializing timer
67 ///</summary>
68 ///<param name=”sender”></param>
69 ///<param name=”e”></param>
70 private void InitTimer_Elapsed(object state)
71 {
72 //import is executed one time only in this eventhandler.
73 try
74 {
75 //Start the schedule
76 StartScheduleTimer();
77 //perform the job
78 DoSomething();
79
80 }
81 catch (Exception ex)
82 {
83
84 }
85 finally
86 {
87 //dispose the timer object – this timer has done it’s purpose
88 initTimer.Dispose();
89 }
90
91 }
100 private void StartScheduleTimer()
101 {
102 //the interval in hours
103 // s m h
104 int timerInterval = int.Parse(ConfigurationManager.AppSettings[“TimerInterval”]) * 1000 * 60 * 60;
105 scheduleTimer = new System.Threading.Timer(ScheduleTimer_Elapsed, null, timerInterval, System.Threading.Timeout.Infinite);
106 }
107
108
109 ///<summary>
110 /// Every time the timer ticks – the ScheduleTimer_Elapsed method is triggered – the job stuff is being performed here
111 ///</summary>
112 ///<param name=”sender”></param>
113 ///<param name=”e”></param>
114 private void ScheduleTimer_Elapsed(object state)
115 {
116 //reset the timer – set the due/wait time until next event firing
117 // s m h
118 scheduleTimer.Change(int.Parse(ConfigurationManager.AppSettings[“TimerInterval”]) * 1000 * 60 * 60, System.Threading.Timeout.Infinite);
119 //perform the job
120 DoSomething();
121 }