Click here to Skip to main content
15,390,470 members
Articles / Programming Languages / C# 4.0
Article
Posted 16 May 2013

Stats

17.1K views
25 bookmarked

Build Your Own Scheduler using IObservable<T>

Rate me:
Please Sign up or sign in to vote.
5.00/5 (4 votes)
16 May 2013CPOL4 min read
This article will help you to build your own scheduler with the flavour of Observable Design Pattern.

Introduction

This article will help you to build a Scheduler that you can easily attach with your .NET Application. By using this scheduler, you can configure your schedule time and also manage modules with minimum afford. Here, I will show you how Observer design pattern helps to make a manageable scheduler.

Background

Before reading this article, all of you must have basic knowledge about Observer Design Pattern. It will help a lot and also make this article easy and interesting to you. By the way, here is a typical description.

"Define a one-to-many dependency between objects so that when one object changes state, all its dependents are notified and updated automatically."

Image 1

Fig: 1.1 Class diagram for observer pattern.

In short, if I want to explain:

Concrete Subject inherits Subject and Observer inherits Concrete Observer.

If the Concrete Subject changes, it will notify to others.

Who are the others?

Others means those who are attached with the Concrete Subject.

So finally, Concrete Subject changes notify the attached modules.

Using the Code

Guess I need to build a scheduler which will send email, text SMS and upload files to FTP server. So from my previous discussion, I can say that I need to make a schedule-task (Concrete Subject) whose responsibility (attached) is to send those mails. In .NET 4.0, we have two nice Interfaces.

  1. IObservable<T>
  2. IObserver<T>

If you look at those Interfaces, you will notice that it has almost the same methods like our subject and observer in fig 1.1.

IObservable<T> has only one method:

C#
Disposable Subscribe(IObserver<T> observer)  

If compared with our attached method, only the return type makes the difference. So we don't need any detach method here. Why? Because it returns a disposable object of subscribe object. If you are still confused, no problem, I will explain it later in my discussion.

IObserver<T> has three methods:

C#
void OnCompleted()
void OnError(Exception error)
void OnNext(T value)

OnNext (T value) is our main concern. It works like Update method on Observer fig 1.1.

One thing that really surprised me where is the Notify of the IObservable<T>? Ok, let's get back to our scheduler jobs.

So we need a class for implementing the IObservable<T>. Below is the class:

C#
public class Observable<T> : IObservable<T>
    {
        List<IObserver<T>> observers = new List<IObserver<T>>();
 
        public Observable()
        {           
            observers = new List<IObserver<T>>();
        }
 
        protected void Notify(T obj)
        {
            foreach (IObserver<T> observer in observers)
            {
                observer.OnNext(obj);
            }
        }
 
        public IDisposable Subscribe(IObserver<T> observer)
        {
            if (!observers.Contains(observer))
            {
                observers.Add(observer);
            }
 
            return new Unsubscriber(observers, observer);
        } 
 
        private class Unsubscriber : IDisposable
        {
            private List<IObserver<T>> observers;
            private IObserver<T> observer;
 
            public Unsubscriber(List<IObserver<T>> observers, IObserver<T> observer)
            {
                this.observers = observers;
                this.observer = observer;
            }
 
            public void Dispose()
            {
                if (observer != null && observers.Contains(observer))
                {
                    observers.Remove(observer);
                }
            }
        }
    }

So we have the generics class with the Notify method. Here, you can see how you can get the IDisposable after subscribe. Now we can consider the Observable<T> class as Subject. Now we need our concrete subject.

C#
public class SchedulerTask : Observable<SchedulerTask>
    {
        bool _switchOn;
  
        public bool SwitchOn
        {
            get
            {
                return _switchOn;
            }
            set
            {
                _switchOn = value;
                if (_switchOn)
                {
 
                    Notify(this);
                }
            }
        } 
 
        public SchedulerTask()
        {
            
        }
    }

So our SchedulerTask class is designed in a way that if property value SwitchOn sets true, it will call the base classes Notify method and all the subscribe modules will be notified.

So for three tasks, we need the classes that will process or execute the task. And they will be notified by the SchedularTask class when update happens. To receive the notification (OnNext(T)), they need to implement IObserver<T>.

This is for MailSend:

C#
public class SendNotificationMail : IObserver<SchedulerTask>
    {
        public SendNotificationMail()
        {
 
        }
 
        public void OnCompleted()
        {
            throw new NotImplementedException();
        }
 
        public void OnError(Exception error)
        {
            throw new NotImplementedException();
        }
 
        public void OnNext(SchedulerTask value)
        {
            //Task that needs to be done on schedule time
            // ProcessSendNotificationMailMails(value);
        }        
    }

This is for TextSMS:

C#
public class SendTextMessaage : IObserver<SchedulerTask>
    {
        public SendTextMessaage()
        {
 
        }
        public void OnCompleted()
        {
            throw new NotImplementedException();
        }
 
        public void OnError(Exception error)
        {
            throw new NotImplementedException();
        }
 
        public void OnNext(SchedulerTask value)
        {
            //Task that needs to be done on schedule time
            // ProcessSendTextMessaage(value);           
        }        
    }   

This is for Upload file:

C#
public class UpLoadFileFromFtp :IObserver<SchedulerTask>
    {
        public UpLoadFileFromFtp()
        {
 
        }
        public void OnCompleted()
        {
            throw new NotImplementedException();
        }
 
        public void OnError(Exception error)
        {
            throw new NotImplementedException();
        }
 
        public void OnNext(SchedulerTask value)
        {
            //Task that needs to be done on schedule time
            // ProcessUpLoadFileFromFtp(value);
        }
    }

Here, I am not showing the full implementation for the send mail SMS or fileupload. So we have all of the modules. But still one important thing is left. Did you notice that? No, the subscription or attachment of the ScheduerTask with SendNotificationMail, SendTextMessaage and UpLoadFileFromFtp.

To manage those classes, we need a manager class right which will integrate those with each other.

C#
public class ManageScheduleTasks
   {
       SchedulerTask objScheduler;
       Action<bool> TriggerScheduler;

       public ManageScheduleTasks()
       {
           Register();
       }
       protected  void Register()
       {
           objScheduler = new SchedulerTask();
           GetSubsCribtionList().ForEach(p =>
           {
               objScheduler.Subscribe(p);

           });
           TriggerScheduler = InvokeScheduler;
       }

       protected List<IObserver<SchedulerTask>> GetSubsCribtionList()
       {
           return new List<IObserver<SchedulerTask>>()
           {
               new SendNotificationMail(),
               new SendTextMessaage(),
               new UpLoadFileFromFtp()
           };
       }
       public void StartScheduler()
       {
           double dblmilisec = ConfigurationManager.AppSettings["scheduleTimeinMinute"] != null ?
               Convert.ToDouble(ConfigurationManager.AppSettings["scheduleTimeinMinute"])
               * 60000 : -1;
           System.Timers.Timer t = new System.Timers.Timer();
           t.Elapsed += new System.Timers.ElapsedEventHandler(IsTimeToStartScheduler);
           t.Interval = dblmilisec;
           t.Enabled = true;
           t.AutoReset = true;
           t.Start();
       }

       private void IsTimeToStartScheduler(object sender, System.Timers.ElapsedEventArgs e)
       {
           var start = ConfigurationManager.AppSettings["scheduleStartTime"] != null ?
               Convert.ToString(ConfigurationManager.AppSettings["scheduleStartTime"]) : "";
           var triger = start.Equals(DateTime.Now.ToString("hh:mm tt"));
           TriggerScheduler(triger);
       }
       private void InvokeScheduler(bool isStartTime)
       {
           objScheduler.SwitchOn = isStartTime;
       }
   }

Don't worry looking at the manager class. I will explain each and every method step by step.

First of all, look at the constructor of the ManageScheduleTasks class. what does it do? It calls the Register method. The Register method actually subscribes to all the modules (by calling the function GetSubsCribtionList) that will be notified by the SchedulerTask. TriggerScheduler is just an Action<bool>. I will explain it too. So far so good, right.

C#
public ManageScheduleTasks()
        {
            Register();
        }
        protected  void Register()
        {            
            objScheduler = new SchedulerTask();
            GetSubsCribtionList().ForEach(p =>
            {
                objScheduler.Subscribe(p); // Subscribe all the module

            });
            TriggerScheduler = InvoveScheduler;
        }
 
        protected List<IObserver<SchedulerTask>> GetSubsCribtionList()
        {
            return new List<IObserver<SchedulerTask>>()
            {                  
                new SendNotificationMail(),  // Return the list of modules
                new SendTextMessaage(),     // that will be notified during
                new UpLoadFileFromFtp()    //Scheduler process  running                         
            }; 
        }

Now, a question might comes on your mind that what is the use of other three methods StartScheduler, IsTimeToStartScheduler, InvoveScheduler? Below is the answer:

StartScheduler: This method introduces a timer that calls a method IsTimeToStartScheduler after every 1 minute. The interval value comes from the web.config scheduleTimeinMinute. Here, I set 1 .

XML
<appSettings>
    <add  key="scheduleTimeinMinute" value="1"/>
    <add   key="scheduleStartTime" value="01:00 AM"/>
  </appSettings>
C#
public void StartScheduler()
        {
            double dblmilisec = ConfigurationManager.AppSettings["scheduleTimeinMinute"] != null ?
                Convert.ToDouble(ConfigurationManager.AppSettings["scheduleTimeinMinute"]) 
                * 60000 : -1;
            System.Timers.Timer t = new System.Timers.Timer();
            t.Elapsed += new System.Timers.ElapsedEventHandler(IsTimeToStartScheduler);
            t.Interval = dblmilisec;
            t.Enabled = true;
            t.AutoReset = true;
            t.Start();
        }

IsTimeToStartScheduler: This method will get the exact time to start the scheduler from the web.config scheduleStartTime and check whether it is time to start the scheduler. At 01:00AM, it will set the value of var trigger is true. 01:00AM comes from my web.config schedulerStartTime.

XML
<appSettings> 
<add key="scheduleTimeinMinute" value="1"/> 
<add key="scheduleStartTime" value="01:00 AM"/> 
</appSettings>  

If the value of var trigger is true, then the scheduler starts. Confused how it will start? Below is the explanation.

C#
private void IsTimeToStartScheduler(object sender, System.Timers.ElapsedEventArgs e)
       {
           var start = ConfigurationManager.AppSettings["scheduleStartTime"] != null ?
               Convert.ToString(ConfigurationManager.AppSettings["scheduleStartTime"]) : "";
           var triger = start.Equals(DateTime.Now.ToString("hh:mm tt"));
           TriggerScheduler(triger);
       }

So as I told you earlier, TriggerScheduler is an action that points to the InvokeScheduler and it is actually setting the SwitchOn value. So you remember the property when it calls the notify? When it sets value to true.

C#
private void InvoveScheduler(bool isStartTime)
    {
        objScheduler.SwitchOn = isStartTime;
    }
C#
public bool SwitchOn
       {
           get
           {
               return _switchOn;
           }
           set
           {
               _switchOn = value;
               if (_switchOn)
               {

                   Notify(this);
               }
           }
       }

Points of Interest

So, how do we hookup my ManageScheduleTasks class with Http? Here is the trick - use your Global.asax.cs class and put the below code on Application_Start:

C#
public class Global : System.Web.HttpApplication
    {
 
        protected void Application_Start(object sender, EventArgs e)
        {
            var manageSchdule = new ManageScheduleTasks();
            manageSchdule.StartScheduler();
        }        
    }

That's it! Your scheduler is now attached with your application.

This Scheduler will call on a specific time of the day. But if you want to call after every (1,2.3...n) minute, you just do the following and change the value of scheduleTimeinMinute to any minute interval you need.

C#
private void IsTimeToStartScheduler(object sender, System.Timers.ElapsedEventArgs e)
        {
            
            TriggerScheduler(true);
        } 

So we finally developed a scheduler.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

Share

About the Author

Faisal(mfrony)
Bangladesh Bangladesh
I am a Sr.Software Engineer at Brain Station -23. I have 5+ years of work experience in .Net technology. I strongly believe that before software can be reusable it first has to be usable.

My contact info :

mfrony2003@yahoo.com
mfrony2003@hotmail.com

LinkedIn
http://www.linkedin.com/profile/view?id=106671466&trk=tab_pro

Comments and Discussions

 
QuestionCan you provide the source code? Pin
Sudhir Dutt Rawat10-Nov-14 6:57
MemberSudhir Dutt Rawat10-Nov-14 6:57 
QuestionAny source code about? Pin
Fabio Malpezzi13-Jan-14 21:29
professionalFabio Malpezzi13-Jan-14 21:29 
AnswerRe: Any source code about? Pin
Faisal(mfrony)15-Jan-14 4:02
MemberFaisal(mfrony)15-Jan-14 4:02 
Yes i can give you am example. Pls give me you mail address
Thanks
Faisal
GeneralRe: Any source code about? Pin
Abhishek Pant9-Jun-14 5:04
professionalAbhishek Pant9-Jun-14 5:04 

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

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