Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

A Task Scheduler Library for .NET Applications

0.00/5 (No votes)
11 Jun 2014 1  
This is an alternative for A Task Scheduler Library for .NET Applications

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.

// Used to schedule tasks to run on regular intervals or on specific time of everyday.
public abstract class SchedulerBase<T> : Timer, IDisposable
{
    // Returns a new instance of timer and starts it
    // param name="Time": Interval time to execute the timer
    // param name="Trigger": Action to call in a new Thread when timer elapses
    protected SchedulerBase(TimeSpan Time, Action Trigger)
    {
        this.Time = Time;
        Reset();
        Elapsed += (s, e) => Task.Factory.StartNew(Trigger);
    }

    // Returns a new instance of timer and starts it
    // param name="Time": Interval time to execute the timer
    // param name="Trigger": Function to call in a new Thread when timer elapses
    // param name="CallBackFn": Optional call back Action to execute after completing the Trigger function. 
    // Input parameter of this Action is the result of Trigger function
    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; }

    // Resets and restarts the scheduler
    public void Reset()
    {
        Stop();
        Interval = GetInterval();
        Enabled = true;
        Start();
    }

    // A template method to return TimeSpan difference of the next schedule time with respect to current time
    protected abstract double GetInterval();

    // Stop and dispose the timer
    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.

// Scheduler which triggers at a specific time interval
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.

// Scheduler which runs at a specific time everyday.
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()
    {
        // Scheduled time of today
        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) // If nextRunOn time is already past, set to next day
        {
            nextRunOn = nextRunOn.AddDays(1);
        }
        return (nextRunOn - DateTime.Now).TotalMilliseconds;
    }
}

You can create schedulers simply calling specific constructors.

// A function that executes every 5 minutes
SchedulerBase<dynamic> FiveMinScheduler = 
new IntervalScheduler<dynamic>(TimeSpan.FromMinutes(5), FiveMinScheduledFn);

// A function that executes only once in a day at a specific time
SchedulerBase<dynamic> NightScheduler = 
new TimeScheduler<dynamic>(TimeSpan.FromHours(2), NightScheduledFn);

// Using parameters in a schedule function
SchedulerBase<dynamic> ParamerizedScheduler = 
new IntervalScheduler<dynamic>(TimeSpan.FromDays(7), () => ParamerizedScheduledFn(i, 6));

// Using result of scheduled function in another callback function
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.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here