First time at my blog? Check out the table of contents! x
posted on Tuesday, May 20, 2008 7:54 PM |

In a previous article I showed you how to write a custom DotNetNuke SchedulerClient (also known as a Scheduled Task). In this follow up article I will show you how to add, remove, and update your new SchedulerClient within your DotNetNuke installation programmatically, so that you can automate the installation of your custom SchedulerClient. Using this method you can avoid having to manually configure schedule items from within your DotNetNuke site's Host > Schedule menu.

The DotNetNuke.Services.Scheduling.SchedulingProvider Class

If you are going to be programmatically modifying schedule items in a DotNetNuke installation you are going to want to get to know the SchedulingProvider class. Unlike many of the other facilities that the DotNetNuke Framework provides, there does not exist a controller class that handles the dirty work for managing the scheduled items. Instead, the DotNetNuke Framework relies on a provider model for managing the scheduled task system.

I won't go too far into the details of the provider model, but in essence  this design pattern prevents the DotNetNuke Framework from becoming dependent on one particular class. So, in theory, the implementation of the provider can be swapped out with another, without any change to the DotNetNuke core code. Such is the case with the SchedulingProvider class.

The DotNetNuke.Services.Scheduling.SchedulingProvider class has two primary purposes. The first job of this class is to define all the methods that an implementation of a scheduling provider must produce. SchedulingProvider acts as an abstract class from which all concrete provider implementation must inherit.  Here is a screen shot of the methods, properties, and fields which the abstract SchedulingProvider class defines:

image

Notice that there are a few methods that we are likely to care about. For instance the AddSchedule(), DeleteSchedule(), and UpdateSchedule() methods define the behaviors for which a concrete provider will be able to add, delete, and update schedule items. Also note that there is an odd method named Instance() that returns a SchedulingProvider object. This method provides the second job of the SchedulingProvider class.

The SchedulingProvider.Instance() Method

The second role of the SchedulingProvider class is to provide access to the one, singleton, instance of the concrete SchedulingProvider. When the DotNetNuke Framework executes, one and only one instance of the concrete SchedulingProvider is loaded into memory. This is the instance that you must use in order to access and modify schedule items.

So, you may ask, how do you access this singleton instance of the concrete SchedulingProvider? You get it by calling the SchedulingProvider.Instance() method.

The SchedulingProvider.Instance() method is a static method, meaning you call it on the class itself, rather than calling it on an instance of the class. So you can get a reference to the singleton like this if you need to:

SchedulingProvider singleton = SchedulingProvider.Instance();

You can alternatively call methods directly on the singleton without creating a new variable. Like so:

SchedulingProvider.Instance().MethodName();

Where MethodName() is the name of the method you wish to execute on the singleton.

So in summary, the SchedulingProvider class is an abstract class that defines the management methods necessary for schedule management. All concrete SchedulingProvider classes must inherit from the SchedulingProvider class and override all the appropriate methods. The way to access the instance of the functioning concrete SchedulingProvider is to call the Instance() method on the abstract SchedulingProvider class.

Moving On

Let's take a look at the information that you are really here for: How to manage Schedule Items programmatically.

As I hinted at previously, you will be calling methods on the SchedulingProvider instance in order to add, remove, and update schedule items. All of these methods take as a parameter an instance of the ScheduleItem class. So let us take a look at the ScheduleItem class for a moment.

The DotNetNuke.Services.ScheduleItem Class

The ScheduleItem class is fairly straight forward. This class represents a single entry that you see within your DotNetNuke Host > Scheduling control panel section.

image

image

Each property in this class simply wraps a setting for the entry in the scheduling system. These properties also each map to a column in the Schedule database table.

image

As you might recall from my previous article, the scheduled task settings hash table is not fully implemented as of DotNetNuke 4.8. So avoid calling the AddSetting() method on the ScheduleItem class unless you enjoy seeing exceptions thrown in your module. Maybe someone can create a request in the DNN BugTracker  to finish this feature.

Adding a new Schedule Item

Now that you understand all the players in the game, we can add a new Schedule Item programmatically. First you create an instance of ScheduleItem, and populate its properties like so:

ScheduleItem item = new ScheduleItem();

// fully quallified class name and binary name
item.TypeFullName = _TypeFullName;

// execution frequency 
item.TimeLapse = 5;

// units of TimeLapse - "s" for seconds, "m" for minutes, "h" for hours, "d" for days
item.TimeLapseMeasurement = "m";

// retry fequency
item.RetryTimeLapse = 10;

// unit of RetryTimeLapse - "s" for seconds, "m" for minutes, "h" for hours, "d" for days
item.RetryTimeLapseMeasurement = "s";

// num of schedule history events to keep
item.RetainHistoryNum = 50;

// run on event, for instance "APPLICATION_START" or "None"
item.AttachToEvent = "None";

// enable / disable catch up
item.CatchUpEnabled = false;

// enable the scheduled task itself
item.Enabled = true;

// add object dependencies - for example "SiteLog,Users,UsersOnline"
item.ObjectDependencies = "";

// define which servers task should run on - (this is optional of course)
item.Servers = "";

Then you call the AddSchedule() method on the ScheduleProvider instance, passing in the new ScheduleItem:

SchedulingProvider.Instance().AddSchedule(item);

You will now have a new Schedule Item in your DNN scheduling system.

Removing a Schedule Item

Removing a schedule item is just as simple as adding one. You first need to get an instance of a ScheduleItem that contains the ID of the ScheduleItem that you wish to remove. You can do this two ways:

ScheduleItem item = SchedulingProvider.Instance().GetSchedule(id);

or

ScheduleItem item = new ScheduleItem();
item.ScheduleID = id;

In either case you need to know the id of the ScheduleItem to remove. If you don't know the id, you can get an ArrayList of all the ScheduleItems in the DNN installation by calling the GetSchedule() method on the SchedulingProvider instance, without passing any parameters. You can then examine each of the ScheduleItems individually to determine which is the one you need to remove.

Finally, to remove the ScheduleItem you need to pass the ScheduleItem instance to the DeleteSchedule() method of the SchedulingProvider instance.

private int GetIDOfScheduleClient(string typeFullName)
{
    // get array list of schedule items
    ArrayList schduleItems = SchedulingProvider.Instance().GetSchedule();

    // find schedule item with matching TypeFullName
    foreach (object item in schduleItems)
    {
        if (((ScheduleItem)item).TypeFullName == typeFullName)
        {
            return ((ScheduleItem)item).ScheduleID;
        }
    }
    return -1;
}

Updating a Schedule Item

To update a schedule item you call the UpdateSchedule() method on the SchedulingProvider instance and pass an instance of the updated ScheduleItem. The example below gets a ScheduleItem from the provider instance, modifies it, and submits the changes back to the provider:

// get the item by id
ScheduleItem item = SchedulingProvider.Instance().GetSchedule(id);

// set property on item
item.TimeLapse = 60;

// update item
SchedulingProvider.Instance().UpdateSchedule(item);

In Summary

In summary, you can add, remove, and update ScheduleItems by calling the AddSchedule(), DeleteSchedule(), and UpdateSchedule() methods respectively on the singleton instance of the SchedulingProvider. In each case you need to pass an instance of ScheduleItem that has been populated with the appropriate data.

Please comment or contact me if you have any questions or would like to add some additional knowledge that is missing from this article.

Working Examples

Here is a working module for your reference. This module installs the ScheduledTaskExample SchedulerClient that can be found in this post. You will need to have the appropriate SchedulerClient dll file in your bin directory in order to run this module.

ScheuleClientInstallerCS.aspx:

<%@ Control Language="C#" 
    AutoEventWireup="true" 
    CodeFile="ScheduleClientInstallerCS.ascx.cs"
    Inherits="Kemmis.Examples.DotNetNuke.ScheduleClientInstallerCS" %>
<div style="text-align: center">
    <asp:Button ID="Button1" 
        runat="server" 
        Text="Install ScheduledTaskExample" 
        OnClick="Button1_Click"
        CommandArgument="add" />
</div>

ScheduleClientInstallerCS.aspx.cs

using System;
using System.Collections;
using System.Web.UI.WebControls;

using DotNetNuke.Entities.Modules;
using DotNetNuke.Services.Scheduling;

namespace Kemmis.Examples.DotNetNuke
{
    public partial class ScheduleClientInstallerCS : PortalModuleBase
    {
        private const string _TypeFullName = "Kemmis.Examples.DotNetNuke.ScheduledTaskExample,DotNetNukeScheduledTaskExampleCS";

        protected void Page_Load(object sender, EventArgs e)
        {
            if (!IsPostBack && GetIDOfScheduleClient(_TypeFullName) > 0)
            {
                Button1.CommandArgument = "delete";
                Button1.Text = "Delete ScheduledTaskExample Item"; 
            }
        }

        protected void Button1_Click(object sender, EventArgs e)
        {
            Button btn = (Button)sender;
            switch (btn.CommandArgument)
            {
                case "add":
                    InstallScheduleClient();
                    btn.CommandArgument = "update";
                    btn.Text = "Update ScheduledTaskExample Item";
                    break;
                case "update":
                    UpdateScheduleItem(GetIDOfScheduleClient(_TypeFullName));
                    btn.CommandArgument = "delete";
                    btn.Text = "Delete ScheduledTaskExample Item";
                    break;
                case "delete":
                    RemoveScheduleClient(GetIDOfScheduleClient(_TypeFullName));
                    btn.CommandArgument = "add";
                    btn.Text = "Install ScheduledTaskExample Item";
                    break;
            }
        }

        private void InstallScheduleClient()
        {
            ScheduleItem item = new ScheduleItem();
            
            // fully quallified class name and binary name
            item.TypeFullName = _TypeFullName;
            
            // execution frequency 
            item.TimeLapse = 5;
            
            // units of TimeLapse - "s" for seconds, "m" for minutes, "h" for hours, "d" for days
            item.TimeLapseMeasurement = "m";

            // retry fequency
            item.RetryTimeLapse = 10;

            // unit of RetryTimeLapse - "s" for seconds, "m" for minutes, "h" for hours, "d" for days
            item.RetryTimeLapseMeasurement = "s";

            // num of schedule history events to keep
            item.RetainHistoryNum = 50;

            // run on event, for instance "APPLICATION_START" or "None"
            item.AttachToEvent = "None";

            // enable / disable catch up
            item.CatchUpEnabled = false;

            // enable the scheduled task itself
            item.Enabled = true;

            // add object dependencies - for example "SiteLog,Users,UsersOnline"
            item.ObjectDependencies = "";

            // define which servers task should run on - (this is optional of course)
            item.Servers = "";

            // add item
            SchedulingProvider.Instance().AddSchedule(item);
        }

        private void RemoveScheduleClient(int id)
        {
            // get the item by id
            ScheduleItem item = SchedulingProvider.Instance().GetSchedule(id);

            // tell the provider to remove the item
            SchedulingProvider.Instance().DeleteSchedule(item);
        }

        private void UpdateScheduleItem(int id)
        {
            // get the item by id
            ScheduleItem item = SchedulingProvider.Instance().GetSchedule(id);

            // set property on item
            item.TimeLapse = 60;

            // update item
            SchedulingProvider.Instance().UpdateSchedule(item);
        }
        private int GetIDOfScheduleClient(string typeFullName)
        {
            // get array list of schedule items
            ArrayList schduleItems = SchedulingProvider.Instance().GetSchedule();

            // find schedule item with matching TypeFullName
            foreach (object item in schduleItems)
            {
                if (((ScheduleItem)item).TypeFullName == typeFullName)
                {
                    return ((ScheduleItem)item).ScheduleID;
                }
            }
            return -1;
        }

    }
}

 

Comments Leave Yours...
bingo fan
7/13/2008 1:31 PM
# bingo fan

Great site - this info is great! Looking forward to reading the rest.

Jason T. Powell
7/23/2008 2:37 PM
# re: Programmatically Add, Delete, and Update Scheduled Tasks

Just stumbled upon your article. Good article.
Two things to note:
1. It doesn't have to be compiled. It can run fine in App_Code the name you'll enter when adding the task in the scheduler will start with App_Code.FolderName.FileName.cs This is great because this means you can fully debug your scheduled task

2.The settings and it's hash table work fine but use AddScheduleItemSetting() not AddSetting()
aScheduler.AddScheduleItemSetting(this.ScheduleHistoryItem.ScheduleID, "lastsuccess", DateTime.Now.ToString());

Getting that value back
if (scheduleSettings.ContainsKey("lastsuccess"))
{ DateTime.TryParse(scheduleSettings["lastsuccess"].ToString(), out lastSuccess);
}

private DNNScheduler aScheduler = new DNNScheduler();
private Hashtable scheduleSettings = new Hashtable();

this.ScheduleHistoryItem = objScheduleHistoryItem;
scheduleSettings = aScheduler.GetScheduleItemSettings(this.ScheduleHistoryItem.ScheduleID);

Rafe
7/23/2008 3:11 PM
# re: Programmatically Add, Delete, and Update Scheduled Tasks

Thanks for the information Jason! I'm sure many people will find it useful!

Larry Daniele
9/30/2008 2:39 PM
# How to do this once for a module?

Hi Rafe,

Thanks for a great article. Very useful! I appreciate all the time you spent to clearly illustrate this with a working example.

Do you have any advice on a smart way for a module to add a scheduled item once (and only once) on installation?

The best I could think of was, each time the module's Page_Load() is called, check to see if the scheduled item is already there and if not then add it. Is there some smarter way to do this?

Larry Daniele
Sunderland, MA USA


OldTurtle
11/10/2008 3:28 AM
# re: Programmatically Add, Delete, and Update Scheduled Tasks

Thank you, solved our headaches :)...
Keep up good job.

Post Your Comment

Title
Required
Name
Required
Email
Optional
Url
Optional
Comment  
Please add 7 and 3 and type the answer here:

Who Is Rafe

rafe

Rafe Kemmis

I am an audacious web developer with a double bachelor of science in Computer Science and Mathematics. I specialize in Microsoft ASP.Net, Silverlight, and Adobe ActionScript.

Questions?

Always a thoughtful response. You may post your question on an article, or contact me directly.

Hire Me.

I provide custom solutions to complex problems. I can help your business no matter how large or small.

Contact me now.

Subscribe