Creating a Lightweight Timer Job for a Site Collection:
Defining Custom Work Items
Timer jobs run in a specific Windows services for the SharePoint Server (please refer to: http://technet.microsoft.com/en-us/library/cc678870.aspx for more information). SharePoint provides some built-in timer job definitions for infrastructure, which provides developers a mechanism to customize jobs. These jobs allow developers to incorporate specific aspects of their business to the job definitions.
- To customize a timer job, you need to extend your own job definition class from the SPJobDefinition class (please refer to: http://msdn.microsoft.com/en-us/library/microsoft.sharepoint.administration.spjobdefinition.aspx for more information).
- You can include your own code in the Execute Method.
- Then you need to associate the timer job with a service or web application.
- Your code will be executed by the SharePoint Timer Service. In general, a timer job is very convenient and useful for businesses based on web applications or services.
- You can access the web application or timer job service execution context very easily. Just use the Service or WebApplication properties to return the SPService or SPWebApplication objects, which this job definition is associated.
- Sometimes, a timer job based on a web application or service is too heavy for a business. Our business is based on site collection. Therefore, we need a more lightweight timer job to process our business needs.
So what we can do?
There is a special timer job in SharePoint defined by the class SPWorkItemJobDefinition, which is inherited from SPPausableJobDefinition (please refer to: http://msdn.microsoft.com/en-us/library/microsoft.sharepoint.administration.sppausablejobdefinition.aspx for more information). This timer job works closely with SPWorkItem, which can add a work item to a site collection.
If we want to create a lightweight timer job, we should define a class inherited from SPWorkItemJobDefinition.
How do we do this?
1. You must override two methods: WorkItemType, ProcessWorkItems
public virtual Guid WorkItemType()
This method returns a GUID that can identify this timer job. When you add a work item to a site collection, you should specify the work item type, so the timer service can process it with the specific timer job definition.
protected virtual bool ProcessWorkItem(
SPContentDatabase contentDatabase,
SPWorkItemCollection workItems,
SPWorkItem workItem,
SPJobState jobState
)
2. This method is really convenient. You can define your custom business logic, send an alert email if the work item has been completed, or schedule a new delivery date.
Adding a work item to a site collection:
3. We can call the method SPSite.AddWorkItem to create a new work item for a site collection. See the following:
public Guid AddWorkItem(
Guid gWorkItemId,
DateTime schdDateTime,
Guid gWorkItemType,
Guid gWebId,
Guid gParentId,
int nItemId,
bool fSetWebId,
Guid gItemGuid,
Guid gBatchId,
int nUserId,
byte[] rgbBinaryPayload,
string strTextPayload,
Guid gProcessingId
)
The method needs some parameters:
gWorkItemId: A GUID that identifies the work item.
schdDateTime: A System.DateTime object that represents the date and time at which the work item takes place.
gWorkItemType: A GUID that specifies the work item type, if you add a custom work item, this should be the return value of the method WorkItemType.
gWebId: A GUID that specifies the Web site that is associated with the work item.
gParentId: A GUID that identifies the parent of the work item.
nItemId: A 32-bit integer that identifies a list item that is associated with the work item.
fSetWebId: true to set the Web identifier; otherwise, false.
gItemGuid: A GUID that specifies a list item that is associated with the work item.
gBatchId: A GUID that specifies a context identifier for the job work item engine. The value can vary depending on the job type; for example, for alerts it might store a user ID but for workflow it would store a workflow ID.
nUserId: A 32-bit integer index that identifies an SPUser object that represents the user who is responsible for scheduling the work item.
rgbBinaryPayload: A byte array that specifies a binary payload.
strTextPayload: A string that specifies a text payload.
gProcessingId: A GUID that identifies the processing of the work item.
4. The following code will show how to define a custom work item (custom site collection timer job) and how to add a work item to a site collection.
public class MyWorkItemJobDefinition : SPWorkItemJobDefinition
{
public MyWorkItemJobDefinition (string name, SPWebApplication webApplication)
: base (name, webApplication)
{
}
public override Guid WorkItemType()
{
return new Guid("6F9619FF-8B86-D011-B42D-00C04FC964FF");
}
public override bool ProcessWorkItem(
SPContentDatabase contentDatabase,
SPWorkItemCollection workItems,
SPWorkItem workItem,
SPJobState jobState)
{
If(workItem != null)
{
using(SPSite site = new SPSite(workItem.SiteId))
{
using(SPWeb web = site.OpenWeb(workItem.WebId)
{
SPUtility.SendEmail(web,
false,
false,
"test@gmail.com",
"test email",
"this is a custom work item test email!");
}
}
//the email will send every day, and of course, you can delete this work item too.
DateTime newDeliverDate = DateTime.UtcNow.AddDays(1);
workItems.UpdateWorkItem( workitem.Id, newDeliverDate, null, "send again");
}
}
}
5. After your timer job was added to a web application we often do this on feature activated), you can add your custom work item to a site collection like this:
site.AddWorkItem( Guid.NewGuid(),
DateTime.UtcNow.AddDays(0.5),
new Guid("6F9619FF-8B86-D011-B42D-00C04FC964FF"); // your work item type
SPConetxt.Current.Web.Id,
SPContext.Current.ListId,
SPContext.Current.ItemId,
true,
SPContext.Current.ListItem.UniqueId,
SPContext.Current.ListItem.UniqueId,
SPContext.Current.Web.CurrentUser.ID,
null,
"Custom work item test",
Guid.Empty);
How does it work?
1. When a work item was added to a site collection, a record will be added to the scheduledworkitems table in the web application’s content database.
2. When timer job is executed, the records in the table meeting the conditions will be loaded and then processed. Because of performance reasons, the timer job will not load any items greater than its BatchFetchLimit property.
3. However, you must manually update / delete the work items that have been processed or else it will just process the items up to the BatchFetchLimit property again and again.
NOTE: I encountered an issue where the timer job only processed the first 100 items in the scheduledworkitems table because I did not delete the completed items. However, their DeliveryDate condition was updated to a very small value, so every time the timer job was executed, the items still met the conditions from last time and the other work items were never processed.
4. If we just need a timer job to work on a site collection, customizing a timer job inherited from the SPWorkItemJobDefinition class is a better way. This kind of timer job can readily be added to a site collection as a work item and the identity of the site collection and Web ID is already stored on the scheduledworkitems table. Additionally, it can be fetched by the timer job directly. The normal timer job does not contain the identity of the site collection or Web ID, so we need to iterate all the sites in the web application, which the timer job is associated.
You said: “You must override two methods: WorkItemType, ProcessWorkItems” but in you example you override “ProcessWorkItem” not “ProcessWorkItems”. In my experience this method never called.