Monday, 15 December 2008

Timer application on .net c#

In some projects i have worked, there have been requiremts of console application which runs on the background and does(requests) some processing at a certain interval of time. For example, if you have a newsletter subscription system in you site, you may want to send news at 8:00 AM every monday. For this, a console application will run as a schedule task or windows service and if set as schedule task, it will runt at that particular scheduled time.

Now, say for example, you newletter subscription for 3 types of newsletters, one is sent every monday 8 A.M, the second one is sent every Monday-Wednesday and the third one is sent everyday. Since, its newsletter and has the same database of subscribers but only differs in the subscripttion type (which definesd which trpe of newsletter to sent to the subscriber). And most importantly you do not want to write 3 different console applications (or timers) with almost same logic.Extending the newsletters, now say, you want to process(or request) a database call every 10 mins. So, we have 4 different implementation at different time intervals.


Requirements:
1. Application running in background like a timer
2. More than one process request with independent time intervals

High level Logic:
1. Get the list of process requests
2. Check if it is time to make that request
3. If it is time then make a request

1. Getting the list of process requests:
Create an XML file with the following settings,

<?xml version="1.0" encoding="utf-8" ?>
<ProcessList>
<Process Name="NewsletterMonday">
<Times>
<Time Value="8:30 AM" ClockType="_12Hr" />
</Times>
<Days Type="Selected">
<Day Value="Monday" />
</Days>
</Process>
<Process Name="NewsletterMondayToWednesday">
<Times>
<Time Value="10:30 PM" ClockType="_12Hr"/>
</Times>
<Days Type="Range">
<Day Value="Monday" />
<Day Value="Wednesday" />
</Days>
</Process>
<Process Name="NewsletterAllDays">
<Times>
<Time Value="15:00" ClockType="_24Hr"/>
</Times>
<Days Type="All" />
</Process>
<Process Name="DatabaseCallEveryTenMinsAllDays">
<Times>
<Time Value="10" ClockType="Min"/>
</Times>
<Days Type="All" />
</Process>
</ProcessList>
This seetings xml defines what type of process are there and the time to make the
process request.

2. Check if it is time to make that request:
private bool isProcessRequestDay()
{
bool isProcessRequestDay= false;
switch (_Days.Type)
{
case DayType.All:
isRefreshDay = true;
break;
case DayType.Range:
isProcessRequestDay = (DateTime.Today.DayOfWeek >= _Days[0].Value && DateTime.Today.DayOfWeek <= _Days[_Days.Count - 1].Value);
break;
case DayType.Selected:
foreach (Day day in _Days)
{
if (day.Value == DateTime.Today.DayOfWeek)
{
isProcessRequestDay= true;
break;
}
}
break;
}
return isProcessRequestDay;
}



_Days is a Generic List class with type as a property which contains
the value from < type="----"> from the settings xml. [Note: You will have
to load the settings to the its corresponding classes. ]

Now, get the date and time at which the process request should be made



    private DateTime getMinProcessRequestDateTime(DateTime currentDateTime)
    {
        int min = 0, hr = 0, sec = 0;
        int year = currentDateTime.Year;
        int month = currentDateTime.Month;
        int day = currentDateTime.Day;
        DateTime minProcessRequestDateTime = DateTime.MinValue;

        foreach (ProcessRequestTime ProcessRequestTime in _ProcessRequestTimes)
        {
            DateTime tempProcessRequestDateTime = DateTime.MaxValue;
            switch (ProcessRequestTime.ClockType)
            {
                case ClockType._24Hr:
                    string[] time24Hr = ProcessRequestTime.Value.Split(Constants.SeperatorColon);
                    hr = Convert.ToInt32(time24Hr[0]);
                    min = Convert.ToInt32(time24Hr[1]);

                    tempProcessRequestDateTime = new DateTime(year, month, day, hr, min, 0);
                    if (!(currentDateTime > tempProcessRequestDateTime))
                    {
                        tempProcessRequestDateTime = tempProcessRequestDateTime.AddDays(-1);
                    }
                    break;
                case ClockType._12Hr:
                    string[] time12Hr = ProcessRequestTime.Value.Split(Constants.SeperatorSingleSpace);
                    if (time12Hr[1].Equals(Constants.DateTimeMeridianPM, StringComparison.InvariantCultureIgnoreCase))
                    {
                        hr = Convert.ToInt32(time12Hr[0].Split(Constants.SeperatorColon)[0]);
                    }
                    else if (time12Hr[1].Equals(Constants.DateTimeMeridianAM, StringComparison.InvariantCultureIgnoreCase))
                    {
                        hr = Convert.ToInt32(time12Hr[0].Split(Constants.SeperatorColon)[0]);
                        if (hr >= 12) { hr -= 12; }
                    }
                    min = Convert.ToInt32(time12Hr[0].Split(Constants.SeperatorColon)[1]);

                    tempProcessRequestDateTime = new DateTime(year, month, day, hr, min, 0);
                    if (!(currentDateTime > tempProcessRequestDateTime))
                    {
                        tempProcessRequestDateTime = tempProcessRequestDateTime.AddDays(-1);
                    }
                    break;
                case ClockType.Hr:
                    hr = Convert.ToInt32(ProcessRequestTime.Value);
                    tempProcessRequestDateTime = new DateTime(year, month, day);
                    tempProcessRequestDateTime = tempProcessRequestDateTime.AddHours(currentDateTime.Hour - hr).AddMinutes(currentDateTime.Minute);
                    break;
                case ClockType.Min:
                    min = Convert.ToInt32(ProcessRequestTime.Value);
                    tempProcessRequestDateTime = new DateTime(year, month, day);
                    tempProcessRequestDateTime = tempProcessRequestDateTime.AddHours(currentDateTime.Hour).AddMinutes(currentDateTime.Minute - min);
                    break;
                case ClockType.Sec:
                    sec = Convert.ToInt32(ProcessRequestTime.Value);
                    tempProcessRequestDateTime = new DateTime(year, month, day);
                    tempProcessRequestDateTime = tempProcessRequestDateTime.AddHours(currentDateTime.Hour).AddMinutes(currentDateTime.Minute).AddSeconds(currentDateTime.Second - sec);
                    break;
            }
            //Assigning the maximum datetime less than the current date
            if (tempProcessRequestDateTime > minProcessRequestDateTime)
            { minProcessRequestDateTime = tempProcessRequestDateTime; _DataSource = ProcessRequestTime.DataSource; }
        }
        return minProcessRequestDateTime;
    }

Now, get the date and time when the last process request was made,
DateTime lastProcessRequestDateTime = {from database or file or memory};



Now, check if last request made before the time calculated for next request,
bool isProcessRequestTime = (minProcessRequestDateTime >= lastProcessRequestDateTime);

If the last request made was before the current calculated time for request, then it means that now is the time to make a new request. Otherwise, if the last request made is after the current calulated time for next request, then the schedule time for process request has already been made.

3. If it is time then make a request
if(isProcessRequestTime)
{
//Call the process
// database call or web service call or http request call
}

Hope this give some logic into how you can write a single application with process request settings that runs on background to make different process request for different time intervals.

If you require code for this example, please mail me or give your mail id in the comment section. I'll will send you the whole code for this application.

No comments:

Post a Comment