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. This is a trial to make an alternative for "A Task Scheduler Library for .NET Applications" having a cleaner design by spliting the Scheduler
class into two classes as suggested by John Brett in this comment.
Using the Code
In this design, there is an abstract
generic class ServiceBase
. I preferred abstract
class over interface
for code reusability.
public abstract class SchedulerBase<T> : Timer, IDisposable
{
protected SchedulerBase(TimeSpan Time, Action Trigger)
{
this.Time = Time;
Reset();
Elapsed += (s, e) => Task.Factory.StartNew(Trigger);
}
protected SchedulerBase(TimeSpan Time, Func<T> Trigger, Action<T> CallBackFn)
{
this.Time = Time;
Reset();
Elapsed += (s, e) => Task.Factory.StartNew(Trigger)
.ContinueWith(prevTask => CallBackFn(prevTask.Result));
}
protected TimeSpan Time { get; set; }
public void Reset()
{
Stop();
Interval = GetInterval();
Enabled = true;
Start();
}
protected abstract double GetInterval();
protected override void Dispose(bool disposing)
{
Stop();
base.Dispose(disposing);
}
}
IntervalScheduler
is one of the concrete classes inheriting from the SchedulerBase
class. This class provides the interval based scheduling behavior.
public class IntervalScheduler<T> : SchedulerBase<T>
{
public IntervalScheduler(TimeSpan Time, Action Trigger) : base(Time, Trigger) { }
public IntervalScheduler(TimeSpan Time, Func<T>
Trigger, Action<T> CallBackFn) : base(Time, Trigger, CallBackFn) { }
protected override double GetInterval()
{
return Time.TotalMilliseconds;
}
}
TimeScheduler
is the other class inherits from SchedulerBase
and provides the behavior to run the scheduler at specific time of a day.
public class TimeScheduler<T> : SchedulerBase<T>
{
public TimeScheduler(TimeSpan Time, Action Trigger) : base(Time, Trigger) { }
public TimeScheduler(TimeSpan Time, Func<T> Trigger,
Action<T> CallBackFn) : base(Time, Trigger, CallBackFn) { }
protected override double GetInterval()
{
DateTime 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;
}
}
You can create schedulers simply calling specific constructors.
SchedulerBase<dynamic> FiveMinScheduler =
new IntervalScheduler<dynamic>(TimeSpan.FromMinutes(5), FiveMinScheduledFn);
SchedulerBase<dynamic> NightScheduler =
new TimeScheduler<dynamic>(TimeSpan.FromHours(2), NightScheduledFn);
SchedulerBase<dynamic> ParamerizedScheduler =
new IntervalScheduler<dynamic>(TimeSpan.FromDays(7), () => ParamerizedScheduledFn(i, 6));
SchedulerBase<string> CallbackScheduler =
new IntervalScheduler<string>(TimeSpan.FromSeconds(30),
() => ParamerizedScheduledFn(i, 6), CallBackFunc);
Some of the scheduled functions used above are:
internal void NightScheduledFn()
{
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));
}
Points of Interest
By comparing the alternative implementation, we can see how splitting the Scheduler
class into two classes can achieve a cleaner design.
See the alternative implementation using factory pattern.