One task that I’ve been threatening to do for ages is to build a SharePoint Timer Job. Often a solution needs a bit of code to run periodically, but so far it wasn’t something that I’d tried. Well, I had a go today following Andrew Connell’s MSDN article, and it was very good. Here are my notes though…
First up, I used WSPBuilder’s project definitions. It’s much easier to build a WSP this way, and the ‘Blank Feature with Receiver’ template is very useful, as I used an SPFeatureReceiver to activate the job.
All my job did was, well, prove it was running by writing to a local log file. I didn’t do all of the stuff Andrew Connell wrote about regarding configuration – while creating an administration page would be an interesting exercise, I don’t think that we’ll do that often in practice. More likely are external configuration files, and that’s pretty simple.
Anyway, other than that the only puzzle was that my Feature is scoped at a farm level, and the example feature receiver wasn’t scoped at this level. So, if I’m working at the farm level, what is my feature’s parent?
Well, some poking around showed this to be the SPWebService. Thus, to delete the job on feature deactivation, the code was:
SPWebService svc = properties.Feature.Parent as SPWebService
foreach (SPJobDefinition job in svc.JobDefinitions)
{
if(job.Name == "MyServiceName")
{
job.Delete();
}
}
Other than that, it was all pretty simple.
Yeah, Connell’s article omitting scope is a huge oversite. I’ve wasted way to much time figuring out scope. I’ve got all this stupid code like:
SPWeb web = props.Feature.Parent as SPWeb;
if (web == null) {
SPSite site = props.Feature.Parent as SPSite;
if (site == null) {
…
You could use:
props.Feature.Definition.Scope
It’ll give a result which is an a value from the SPFeatureScope enumeration:
http://msdn.microsoft.com/en-us/library/microsoft.sharepoint.spfeaturescope.aspx
But your code works too!
Is there any reason to scope a timer job feature at the site or site collection level? I would have thought you would always want to go for Farm or Web Application scope, since the timer job is basically attached either to a single web app or all of them.
I can see you might want to include a timer job within a feature that contains other elements that you might want to scope at site or site collection level, so in that case you could wrap up the timer job within a site collection scope feature.
Oh, yes, I agree – I can’t imagine why you’d want to scope a timer job at a lower level.
Yes, the timer job’s are attached to a single web app, or all of them. I suppose that in principle you might want a timer job per Site Collection (Or Site), and I’m sure you could do that – but yuck!
I’d rather have one job that checks Sites/Collections to see what it is supposed to apply to, and then use a feature to ‘register’ or ‘unregister’ with that service (most likely setting a flag in the site/collection’s property bag)