我们知道SharePoint使用timer job来定时处理一些任务,只需要继承SPJobDefinition类,然后重写Execute方法,就可以创建出自己的timer job了。我们经常使用timer job来处理列表中的item,例如定时更新item的状态等等,这样的操作一般来讲是在Execute方法中获取需要处理的item,然后遍历这些item来执行一些操作。如果我们需要一些更大的灵活性来处理item,比如按照需要动态的添加需要处理的item,指定处理item的时间,或者动态移除一些不再需要处理的item,可以使用SharePoint提供的另一种专门为了批量处理item而设计的timer job:work item timer job (SPWorkItemJobDefinition)。这种方式允许你以队列的的方式批量处理item,而且是异步处理。这种方式相对于常用的timer job,处理item更加灵活方便,可以根据需要将待处理的item添加到队列中或者从队列中移除,指定下次运行时间等等。
首先介绍一下如何创建一个Work Item Timer Job,其实很简单,与创建SPJobDefinition类似,在project中添加一个类WorkItemJob,继承SPWorkItemJobDefinition,然后需要重写一些方法和属性:
public class WorkItemJob : SPWorkItemJobDefinition
{
public static readonly string WorkItemJobDisplayName = "WorkItemJob Demo";
public static readonly Guid WorkItemTypeId = new Guid("{B5597728-498B-4123-9C0E-DA6640CE0BC9}");
public override string DisplayName
{
get
{
return WorkItemJobDisplayName;
}
}
public override Guid WorkItemType()
{
return WorkItemTypeId;
}
public override int BatchFetchLimit
{
get
{
return 50;
}
}
#region 构造函数
public WorkItemJob() { }
public WorkItemJob(string name, SPWebApplication webApplicaiton) : base(name, webApplicaiton) { }
#endregion
protected override bool ProcessWorkItem(SPContentDatabase contentDatabase, SPWorkItemCollection workItems, SPWorkItem workItem, SPJobState jobState)
{
}
}
其中DisplayName是timer job的名字,WorkItemType方法返回的是timer job的唯一标识,这个在之后的AddWorkItem方法中需要使用,BatchFetchLimit是一次timer job运行处理的work item的数量。我们需要重写的是ProcessWorkItem方法来实现自己的逻辑。
有了work item timer job之后,就可以使用SPSite.AddWorkItem这个方法将需要处理的item添加到队列中,这个方法需要的参数比较多,以下代码是使用一个控制台程序,将Tasks列表中的一个item添加到队列中:
class Program
{
static void Main(string[] args)
{
SPSecurity.RunWithElevatedPrivileges(() =>
{
using (SPSite site = new SPSite("http://sp2013test"))
{
using (SPWeb web = site.OpenWeb())
{
SPList list = web.Lists["Tasks"];
SPListItem item = list.GetItemById(1);
SPUser user = web.SiteUsers[@"demo\administrator"];
Guid workItemId = Guid.NewGuid();
site.AddWorkItem(
workItemId,
DateTime.Now.ToUniversalTime(), //指定运行时间,这里需要指定UTC时间
new Guid("{B5597728-498B-4123-9C0E-DA6640CE0BC9}"), //Work item timer job中定义的WorkItemTypeId
web.ID,
list.ID,
item.ID,
true,
item.UniqueId,
item.UniqueId,
user.ID,
null,
"Text you can use in timer job",
Guid.Empty);
}
}
});
}
}
其中各个参数的具体解释可以参考微软文档,需要注意的是,执行AddWorkItem方法需要提升权限到Site Collection Administrator。执行以上代码我们就添加了一个work item,这个work item将会在指定的时间执行,因为现在我们的ProcessWorkItem方法是空的,所以timer job什么都不做。我们加上一些逻辑来更新一个item的title:
protected 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))
{
try
{
SPList list = web.Lists[workItem.ParentId]; //获取list
SPListItem item = list.GetItemByUniqueId(workItem.ItemGuid); //获取待处理的item
string data = workItem.TextPayload; //获取AddWorkItem方法“strTextPayload”参数传入的字符串数据
string newTitle = string.Format("{0} - {1}", data, DateTime.Now.ToLongTimeString());
item["Title"] = newTitle; //更新item的title
item.Update();
//使用UpdateWorkItem的方法,指定下次运行的时间,需要使用UTC时间,这里指定4分钟之后再次运行
workItems.UpdateWorkItem(workItem.Id, DateTime.Now.AddMinutes(4).ToUniversalTime(), null, "run again");
}
catch (Exception)
{
throw;
}
finally
{
//以下注释的代码用来删除workitem,即将其从队列中移除
//workItems.SubCollection(site, web, 0, (uint)workItems.Count).DeleteWorkItem(workItem.Id);
}
}
}
}
return true;
}
需要注意两个地方,一个是在使用AddWorkItem方法添加workitem的时候是可以传递数据给ProcessWorkItem方法的,使用“ strTextPayload”这个参数指定需要传入的字符串。
另外一个是在如果想再次处理这个work item,需要使用UpdateWorkItem方法指定下次运行时间,同时也可以指定TextPayload,如果不需要处理这个work item,可以使用DeleteWorkItem方法将其从队列中删除,具体参见finally代码块。
运行之后,item的title会根据指定的时间而更新,将最新的时间显示在title上:
第一次运行:
第二次运行:
第三次运行:
可以看到,采用work item timer job的方式,不仅可以批量处理item,可以指定下次的运行时间,在处理item的时候很灵活方便。
以下是一些可能使用workitem方式的场景
1. 一些耗时的操作,有的时候用户每创建一个item,都要对应执行一些耗时的操作,比如创建一个相应的站点,访问AD,数据库或者其他资源等等。
2. 定时批量处理item。如果workitem没有从队列中移除,会一直处理下去。因此可以批量更新item的状态等等。
参考:点击打开链接