Click here to Skip to main content
Click here to Skip to main content
Alternative Tip/Trick

A Task Scheduler Library for .NET Applications

, 11 Jun 2014 MIT
Rate this:
Please Sign up or sign in to vote.
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, along with any associated source code and files, is licensed under The MIT License

Share

About the Author

Sen Jacob
Software Developer CCS Technologies
India India
I'm Sen Jacob. Find more info about me at StackOverflow Careers
Follow on   Twitter   Google+   LinkedIn

Comments and Discussions

 
Questionnot installed successfully [modified] PinmemberKetan Mevada12-Jun-14 4:26 
AnswerRe: not installed successfully PinprofessionalSen Jacob12-Jun-14 7:02 
GeneralRe: not installed successfully [modified] PinmemberKetan Mevada16-Jun-14 5:02 
GeneralThoughts PinmemberPIEBALDconsult11-Jun-14 15:18 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Terms of Use | Mobile
Web04 | 2.8.150326.1 | Last Updated 11 Jun 2014
Article Copyright 2014 by Sen Jacob
Everything else Copyright © CodeProject, 1999-2015
Layout: fixed | fluid