Monday, April 4, 2011

Creating Custom SharePoint Timer Jobs !


This post certainly doesn’t walk you through the steps of authoring a custom SharePoint timer job.  It does highlight one issue I ran into though while I was building one recently that seems to be a plain old bug.  After struggling with it for some time, I thought I’d drop a quick post with the hopes it will save someone else the headache! 
The structure of a customer timer job is actually pretty simple – you need a class that inherits from “SPJobDefinition,” that overrides a few constructors as well as the Execute() method.  The Execute() method defines what actually happens when your timer job elapses and gets fired. 
To install your timer job and register in Central Admin, you need a FeatureReceiver.cs class.  It’s not in the scope of this brief post to go through the details of the FeatureReceiver class (but of course email me if you’re stuck!).  The FeatureReceiver for my timer job overrides the “FeatureActivated” and “FeatureDeactivating” events.  The area that was giving me trouble was in the “FeatureActivated” event.  I wanted my timer job to run once per day at 1:45 am.  The code looked something like this:
// Install the job
AggregatorTimerJob aggregatorTimerJob = new AggregatorTimerJob(site.WebApplication);
// Set the schedule - run once daily
SPDailySchedule dailySchedule = new SPDailySchedule();
dailySchedule.BeginHour = 1;
dailySchedule.BeginMinute = 45;
dailySchedule.BeginSecond = 0;
aggregatorTimerJob.Schedule = dailySchedule;
aggregatorTimerJob.Update();

Seems simple enough!  However after hours of experimenting with the SPDailySchedule, and searching for answers, I simply could NOT get this thing to run on a daily schedule.  Most frustrating was that using an “SPMinuteSchedule” or “SPHourlySchedule” worked beautifully!
After much searching I finally concluded that using the “SPDailySchedule” approach seems to universally fail – that seemed to be the consensus amongst the community. 

So … the Workaround

I think this is an easier, more straight-forward approach anyway, so I hesitate to call it a “work-around” at all!  I ended up using the “SPCustomSchedule” class and defining its run schedule as a free-form string parameter as follows.  Works like a charm – on my environment, all the way through production without any issues!
SPSchedule customSchedule = SPSchedule.FromString("daily at 01:45");
aggregatorTimerJob.Schedule = customSchedule;
aggregatorTimerJob.Update();

This post certainly doesn’t walk you through the steps of authoring a custom SharePoint timer job.  It does highlight one issue I ran into though while I was building one recently that seems to be a plain old bug.  After struggling with it for some time, I thought I’d drop a quick post with the hopes it will save someone else the headache! 
The structure of a customer timer job is actually pretty simple – you need a class that inherits from “SPJobDefinition,” that overrides a few constructors as well as the Execute() method.  The Execute() method defines what actually happens when your timer job elapses and gets fired. 
To install your timer job and register in Central Admin, you need a FeatureReceiver.cs class.  It’s not in the scope of this brief post to go through the details of the FeatureReceiver class (but of course email me if you’re stuck!).  The FeatureReceiver for my timer job overrides the “FeatureActivated” and “FeatureDeactivating” events.  The area that was giving me trouble was in the “FeatureActivated” event.  I wanted my timer job to run once per day at 1:45 am.  The code looked something like this:
// Install the job
AggregatorTimerJob aggregatorTimerJob = new AggregatorTimerJob(site.WebApplication);
// Set the schedule - run once daily
SPDailySchedule dailySchedule = new SPDailySchedule();
dailySchedule.BeginHour = 1;
dailySchedule.BeginMinute = 45;
dailySchedule.BeginSecond = 0;
aggregatorTimerJob.Schedule = dailySchedule;
aggregatorTimerJob.Update();

Seems simple enough!  However after hours of experimenting with the SPDailySchedule, and searching for answers, I simply could NOT get this thing to run on a daily schedule.  Most frustrating was that using an “SPMinuteSchedule” or “SPHourlySchedule” worked beautifully!
After much searching I finally concluded that using the “SPDailySchedule” approach seems to universally fail – that seemed to be the consensus amongst the community. 

So … the Workaround

I think this is an easier, more straight-forward approach anyway, so I hesitate to call it a “work-around” at all!  I ended up using the “SPCustomSchedule” class and defining its run schedule as a free-form string parameter as follows.  Works like a charm – on my environment, all the way through production without any issues!
SPSchedule customSchedule = SPSchedule.FromString("daily at 01:45");
aggregatorTimerJob.Schedule = customSchedule;
aggregatorTimerJob.Update();

Sample Example of Custom Timer Jobs
Alert users from 30 days to "Due Date"?

public override void Execute (Guid contentDbId) 
{
  //Get a reference to the current site collection's content database
  SPWebApplication webApplication = this.Parent as SPWebApplication;
  SPContentDatabase contentDb = webApplication.ContentDatabases[contentDbId];
  //Get a reference to the "Tasks" list in the RootWeb of the first site collection in the content database
  using (SPSite spSiteCurrent = contentDb.Sites["sites/abcd"])
  {
    using(SPWeb spWebRoot = spSiteCurrent.RootWeb)
 {
     TimeSpan tsDuration = new TimeSpan(30,0,0,0);
     DateTime dtDueDate = DateTime.Now.Add(tsDuration);
  string strISODueDate = SPUtility.CreateISO8601DateTimeFromSystemDateTime(dtDueDate);
  
  SPList taskList = spWebRoot.Lists["Tasks"];
  //Get all the task items due for completion 30 days from Now
  SPQuery spQryDue = new SPQuery();
  spQryDue.Query = "<Where><Eq><FieldRef Name='Due_x0020_Date'/><Value Type='DateTime'>" + strISODueDate + "</Value></Eq></Where>";
  SPListItemCollection spTaskItemColl = taskList.GetItems(spQry);
         
  StringBuilder sbMessage = new StringBuilder();
  StringDictionary sdMailHeader = new StringDictionary();
  sdMailHeader.Add("To",string.Empty);
  sdMailHeader.Add("Subject","Task Reminder");
    
  foreach(SPListItem spTaskItem in spTaskItemColl)
  {
    string fieldValue = Convert.ToString(task["Assigned To"]);
    SPFieldUserValue assignedTo = (SPFieldUserValue) spTaskItem.Fields["Assigned To"].GetFieldValue(fieldValue);
    
    sbMessage.Append("Hi ");
    sbMessage.Append(assignedTo.User.Name);
    //Build the Email Message
    ........................................
    sdMailHeader["To"] = assignedTo.User.Email;
    SPUtility.SendEmail(spWebRoot,sdMailHeader,sbMessage.ToString(),true);    
  }
 }
  }
 }
Please refer the following link to create and deploy the Timer Jobs in shareepoint :
http://www.andrewconnell.com/blog/articles/CreatingCustomSharePointTimerJobs.aspx

In this link you can find Execute method in TaskLoggerJob class. Write your code to send email alert to the users in this method.

No comments:

Post a Comment