{"id":4379,"date":"2012-06-26T10:12:13","date_gmt":"2012-06-26T02:12:13","guid":{"rendered":"http:\/\/www.sharepointboost.com\/blog\/?p=4379"},"modified":"2023-07-31T11:39:29","modified_gmt":"2023-07-31T03:39:29","slug":"creating-a-lightweight-timer-job-for-a-site-collection-defining-custom-work-items","status":"publish","type":"post","link":"https:\/\/www.boostsolutions.com\/blog\/creating-a-lightweight-timer-job-for-a-site-collection-defining-custom-work-items\/","title":{"rendered":"Creating a Lightweight Timer Job for a Site Collection: Defining Custom Work Items"},"content":{"rendered":"<p>Creating a Lightweight Timer Job for a Site Collection:<\/p>\n<p>Defining Custom Work Items<\/p>\n<p>Timer jobs run in a specific Windows services for the SharePoint Server (please refer to: <a href=\"http:\/\/technet.microsoft.com\/en-us\/library\/cc678870.aspx\" target=\"_blank\" rel=\"noopener noreferrer\">http:\/\/technet.microsoft.com\/en-us\/library\/cc678870.aspx<\/a> 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.<\/p>\n<ol>\n<li>To customize a timer job, you need to extend your own job definition class from the <strong>SPJobDefinition<\/strong> class (please refer to: <a href=\"http:\/\/msdn.microsoft.com\/en-us\/library\/microsoft.sharepoint.administration.spjobdefinition.aspx\" target=\"_blank\" rel=\"noopener noreferrer\">http:\/\/msdn.microsoft.com\/en-us\/library\/microsoft.sharepoint.administration.spjobdefinition.aspx<\/a> for more information).<\/li>\n<li>You can include your own code in the Execute Method.<\/li>\n<li>Then you need to associate the timer job with a service or web application.<\/li>\n<li>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.<\/li>\n<li>You can access the web application or timer job service execution context very easily. Just use the Service or WebApplication properties to return the <strong>SPService<\/strong> or <strong>SPWebApplication<\/strong> objects, which this job definition is associated.<\/li>\n<li>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.<\/li>\n<\/ol>\n<p><span style=\"font-size: small;\"><span style=\"line-height: 24px;\"><!--more--><span style=\"font-weight: bold; text-decoration: underline;\">So what we can do?<\/span><\/span><\/span><\/p>\n<p>There is a special timer job in SharePoint defined by the class <strong>SPWorkItemJobDefinition<\/strong>, which is inherited from <strong>SPPausableJobDefinition<\/strong> (please refer to: <a href=\"http:\/\/msdn.microsoft.com\/en-us\/library\/microsoft.sharepoint.administration.sppausablejobdefinition.aspx\" target=\"_blank\" rel=\"noopener noreferrer\">http:\/\/msdn.microsoft.com\/en-us\/library\/microsoft.sharepoint.administration.sppausablejobdefinition.aspx<\/a> for more information). This timer job works closely with <strong>SPWorkItem<\/strong>, which can add a work item to a site collection.<\/p>\n<p>If we want to create a lightweight timer job, we should define a class inherited from <strong>SPWorkItemJobDefinition.<\/strong><\/p>\n<p><strong> <\/strong><span style=\"text-decoration: underline;\">How do we do this?<\/span><\/p>\n<p><strong><span style=\"text-decoration: underline;\"> <\/span><\/strong>1. You must override two methods: <a href=\"http:\/\/msdn.microsoft.com\/en-us\/library\/ee558455.aspx\" target=\"_blank\" rel=\"noopener noreferrer\">WorkItemType<\/a>, <a href=\"http:\/\/msdn.microsoft.com\/EN-US\/library\/ms435532\" target=\"_blank\" rel=\"noopener noreferrer\">ProcessWorkItems<\/a><\/p>\n<pre lang=\"csharp\"> public virtual Guid WorkItemType()<\/pre>\n<p>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.<\/p>\n<pre lang=\"csharp\">protected virtual bool ProcessWorkItem(\r\n\tSPContentDatabase contentDatabase,\r\n\tSPWorkItemCollection workItems,\r\n\tSPWorkItem workItem,\r\n\tSPJobState jobState\r\n)<\/pre>\n<p>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.<\/p>\n<p><span style=\"text-decoration: underline;\">Adding a work item to a site collection<\/span>:<\/p>\n<p>3. We can call the method <strong>SPSite.AddWorkItem<\/strong> to create a new work item for a site collection. See the following:<\/p>\n<pre lang=\"csharp\">public Guid AddWorkItem(\r\n\tGuid gWorkItemId,\r\n\tDateTime schdDateTime,\r\n\tGuid gWorkItemType,\r\n\tGuid gWebId,\r\n\tGuid gParentId,\r\n\tint nItemId,\r\n\tbool fSetWebId,\r\n\tGuid gItemGuid,\r\n\tGuid gBatchId,\r\n\tint nUserId,\r\n\tbyte[] rgbBinaryPayload,\r\n\tstring strTextPayload,\r\n\tGuid gProcessingId\r\n)<\/pre>\n<p><span style=\"text-decoration: underline;\">The method needs some parameters:<\/span><\/p>\n<p><strong><em>gWorkItemId:<\/em><\/strong> A GUID that identifies the work item.<\/p>\n<p><strong><em>schdDateTime:<\/em><\/strong> A <strong>System.DateTime<\/strong> object that represents the date and time at which the work item takes place.<\/p>\n<p><strong><em>gWorkItemType: <\/em><\/strong>A GUID that specifies the work item type, if you add a custom work item, this should be the return value of the method <strong>WorkItemType<\/strong>.<\/p>\n<p><strong><em>gWebId: <\/em><\/strong>A GUID that specifies the Web site that is associated with the work item.<\/p>\n<p><strong><em>gParentId:<\/em><\/strong> A GUID that identifies the parent of the work item.<\/p>\n<p><strong><em>nItemId: <\/em><\/strong>A 32-bit integer that identifies a list item that is associated with the work item.<\/p>\n<p><strong><em>fSetWebId:<\/em><\/strong> true to set the Web identifier; otherwise, false.<\/p>\n<p><strong><em>gItemGuid: <\/em><\/strong>A GUID that specifies a list item that is associated with the work item.<\/p>\n<p><strong><em>gBatchId: <\/em><\/strong>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.<\/p>\n<p><strong><em>nUserId: <\/em><\/strong>A 32-bit integer index that identifies an <strong>SPUser<\/strong> object that represents the user who is responsible for scheduling the work item.<\/p>\n<p><strong><em>rgbBinaryPayload: <\/em><\/strong>A byte array that specifies a binary payload.<\/p>\n<p><strong><em>strTextPayload: <\/em><\/strong>A string that specifies a text payload.<\/p>\n<p><strong><em>gProcessingId: <\/em><\/strong>A GUID that identifies the processing of the work item.<\/p>\n<p>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.<\/p>\n<pre lang=\"csharp\">public class MyWorkItemJobDefinition : SPWorkItemJobDefinition\r\n{\r\n\tpublic MyWorkItemJobDefinition (string name, SPWebApplication webApplication)\r\n\t\t: base (name, webApplication)\r\n\t{\r\n        }\r\n\r\n        public override Guid WorkItemType()\r\n        {\r\n\treturn new Guid(\"6F9619FF-8B86-D011-B42D-00C04FC964FF\");\r\n        }\r\n\r\n        public override bool ProcessWorkItem(\r\n\t     SPContentDatabase contentDatabase,\r\n\t     SPWorkItemCollection workItems,\r\n\t     SPWorkItem workItem,\r\n\t     SPJobState jobState)\r\n        {\r\n\t     If(workItem != null)\r\n             {\r\n\t          using(SPSite site = new SPSite(workItem.SiteId))\r\n\t          {\r\n\t\t       using(SPWeb web = site.OpenWeb(workItem.WebId)\r\n\t\t       {\r\n\t\t\t    SPUtility.SendEmail(web,\r\n                                  false,\r\n                                  false,\r\n                                  \"test@gmail.com\",\r\n                                  \"test email\",\r\n                                  \"this is a custom work item test email!\");\r\n                       }\r\n                  }\r\n\r\n                 \/\/the email will send every day, and of course, you can delete this work item too.\r\n                 DateTime newDeliverDate = DateTime.UtcNow.AddDays(1);\r\n                 workItems.UpdateWorkItem( workitem.Id, newDeliverDate, null, \"send again\");\r\n            }\r\n       }\r\n}<\/pre>\n<p>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:<\/p>\n<pre lang=\"csharp\">site.AddWorkItem( Guid.NewGuid(),\r\n     DateTime.UtcNow.AddDays(0.5),\r\n     new Guid(\"6F9619FF-8B86-D011-B42D-00C04FC964FF\"); \/\/ your work item type\r\n     SPConetxt.Current.Web.Id,\r\n     SPContext.Current.ListId,\r\n     SPContext.Current.ItemId,\r\n     true,\r\n     SPContext.Current.ListItem.UniqueId,\r\n     SPContext.Current.ListItem.UniqueId,\r\n     SPContext.Current.Web.CurrentUser.ID,\r\n     null,\r\n     \"Custom work item test\",\r\n     Guid.Empty);<\/pre>\n<p><strong>How does it work?<\/strong><\/p>\n<p><strong><\/strong>1. When a work item was added to a site collection, a record will be added to the <strong>scheduledworkitems<\/strong> table in the web application&#8217;s content database.<\/p>\n<p>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 <strong>BatchFetchLimit<\/strong> property.<\/p>\n<p>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 <strong>BatchFetchLimit<\/strong> property again and again.<\/p>\n<p><strong><span style=\"text-decoration: underline;\">NOTE<\/span><\/strong>: I encountered an issue where the timer job only processed the first 100 items in the <strong>scheduledworkitems<\/strong> table because I did not delete the completed items. However, their <strong>DeliveryDate<\/strong> 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.<\/p>\n<p>4. If we just need a timer job to work on a site collection, customizing a timer job inherited from the <strong>SPWorkItemJobDefinition<\/strong> 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 <strong>scheduledworkitems<\/strong> 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.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>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 [&hellip;]<\/p>\n","protected":false},"author":29,"featured_media":0,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":[],"categories":[125],"tags":[405,406,407],"_links":{"self":[{"href":"https:\/\/www.boostsolutions.com\/blog\/wp-json\/wp\/v2\/posts\/4379"}],"collection":[{"href":"https:\/\/www.boostsolutions.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.boostsolutions.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.boostsolutions.com\/blog\/wp-json\/wp\/v2\/users\/29"}],"replies":[{"embeddable":true,"href":"https:\/\/www.boostsolutions.com\/blog\/wp-json\/wp\/v2\/comments?post=4379"}],"version-history":[{"count":18,"href":"https:\/\/www.boostsolutions.com\/blog\/wp-json\/wp\/v2\/posts\/4379\/revisions"}],"predecessor-version":[{"id":9212,"href":"https:\/\/www.boostsolutions.com\/blog\/wp-json\/wp\/v2\/posts\/4379\/revisions\/9212"}],"wp:attachment":[{"href":"https:\/\/www.boostsolutions.com\/blog\/wp-json\/wp\/v2\/media?parent=4379"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.boostsolutions.com\/blog\/wp-json\/wp\/v2\/categories?post=4379"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.boostsolutions.com\/blog\/wp-json\/wp\/v2\/tags?post=4379"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}