Introduction
This is a simple class which can be used for scheduling a task to run at a specific time everyday or to run a task every interval. This can be used for any scheduled tasks like cleaning up files or any other resources, synchronize data to your application from any external data providers, optimize database at regular intervals, send bulk emails or other notifications like SMS in less bandwidth used hours (probably during night time), etc.
Background
I'm using .NET framework >=4.0 class Task in the solution. So check your project is compatible with this utility.
Using the Code
There are two kinds of task scheduling that are available:
public enum ScheduleKind
{
IntervalBased = 0,
TimeBased = 1
}
The main class of interest is the Scheduler
class. This class inherits from System.Timers.Timer class and provides the task scheduling functionality.
public class Scheduler : Timer, IDisposable
{
private Scheduler() { }
private TimeSpan _Time { get; set; }
private ScheduleKind _Kind { get; set; }
public static Scheduler StartNew(TimeSpan Time, Action<datetime> Trigger,
ScheduleKind Kind = ScheduleKind.IntervalBased)
{
Scheduler timer = MakeTimer(Time, Kind);
timer.Elapsed += (s, e) => Task.Factory.StartNew(() => Trigger(e.SignalTime));
return timer;
}
public static Scheduler StartNew<t>(TimeSpan Time, Func<datetime> Trigger,
ScheduleKind Kind = ScheduleKind.IntervalBased, Action<t> CallBackFn = null)
{
Scheduler timer = MakeTimer(Time, Kind);
timer.Elapsed += (s, e) =>
{
if (CallBackFn != null)
{
Task.Factory.StartNew(() => Trigger(e.SignalTime))
.ContinueWith(prevTask => CallBackFn(prevTask.Result));
}
else
{
Task.Factory.StartNew(() => Trigger(e.SignalTime));
}
};
return timer;
}
public static Scheduler StartNew<t, v>(TimeSpan Time, Func<datetime> Trigger,
ScheduleKind Kind = ScheduleKind.IntervalBased, Func<t, v> CallBackFn = null)
{
Scheduler timer = MakeTimer(Time, Kind);
timer.Elapsed += (s, e) =>
{
if (CallBackFn != null)
{
Task.Factory.StartNew(() => Trigger(e.SignalTime))
.ContinueWith(prevTask => CallBackFn(prevTask.Result));
}
else
{
Task.Factory.StartNew(() => Trigger(e.SignalTime));
}
};
return timer;
}
public void Reset()
{
Stop();
switch (_Kind)
{
case ScheduleKind.IntervalBased:
Interval = _Time.TotalMilliseconds;
break;
case ScheduleKind.TimeBased:
Interval = GetTimeDiff();
break;
}
Enabled = true;
Start();
}
private static Scheduler MakeTimer(TimeSpan Time, ScheduleKind Kind = ScheduleKind.IntervalBased)
{
var timer = new Scheduler { _Time = Time, _Kind = Kind };
timer.Reset();
return timer;
}
private double GetTimeDiff()
{
try
{
var nextRunOn =
DateTime.Today.AddHours(_Time.Hours).AddMinutes(_Time.Minutes).AddSeconds(_Time.Seconds);
if (nextRunOn < DateTime.Now)
{
nextRunOn = nextRunOn.AddMinutes(1);
}
if (nextRunOn < DateTime.Now) {
nextRunOn = nextRunOn.AddDays(1);
}
return (nextRunOn - DateTime.Now).TotalMilliseconds;
}
catch (Exception ex)
{
ex.WriteLog();
return 24 * 60 * 60 * 1000;
}
}
protected override void Dispose(bool disposing)
{
Stop();
base.Dispose(disposing);
}
}
Using the Scheduler
class is quite simple.
FiveMinScheduler = Scheduler.StartNew(TimeSpan.FromMinutes(5),
_ => FiveMinScheduledFn());
NightScheduler = Scheduler.StartNew(TimeSpan.FromHours(2), NightScheduledFn, ScheduleKind.TimeBased);
ParamerizedScheduler = Scheduler.StartNew(TimeSpan.FromDays(7),
_ => ParamerizedScheduledFn(i, 6));
CallbackScheduler = Scheduler.StartNew(TimeSpan.FromSeconds(30), _ => ParamerizedScheduledFn(i, 6),
ScheduleKind.IntervalBased, CallBackFunc);
Here is some of the sample methods used in the above example which are passed to the Scheduler.StartNew()
helper method.
internal void NightScheduledFn(DateTime initTime)
{
Helper.WriteLog("I run at 2AM everyday. Current time is: " + DateTime.Now);
}
internal string ParamerizedScheduledFn(int i, int j)
{
Helper.WriteLog(String.Format("Got parameters i={0}
and j={1}. Current time is: {2}", i, j, DateTime.Now));
return (i*j).ToString(CultureInfo.InvariantCulture);
}
internal void CallBackFunc(string result)
{
Helper.WriteLog(String.Format("Scheduled task finished on: {0}.
Result of scheduled task is: {1}", DateTime.Now, result));
}
To stop the Scheduler
, call schedulerObject.Stop()
.
To restart the Scheduler
, call schedulerObject.Reset()
.
Points of Interest
You can find a Windows service with self installer option with the attached source code.
Also checkout the behavioral change implementation using template pattern as suggested by John Brett.
Note: This is my first post on CodeProject. Please suggest ways to improve the tip.