在 SharePoint Foundation 2010 中使用事件接收器(第 2 部分,共 2 部分)
**摘要:**利用 Microsoft SharePoint Foundation 2010 中的事件接收器,您的自定义代码可以在 SharePoint 对象上发生特定操作时响应。本文中的实践示例演示如何使用事件来增强 SharePoint 应用程序。
上次修改时间: 2015年3月9日
适用范围: Business Connectivity Services | Open XML | SharePoint Designer 2010 | SharePoint Foundation 2010 | SharePoint Online | SharePoint Server 2010 | Visual Studio
本文内容
使用事件的实践示例
示例 1:广播通知
示例 2:拆分项目
示例 3:跟踪文档源
示例 4:取消和重定向
示例 5:日志记录事件
结论
其他资源
**供稿人:**Ali Badereddin,Microsoft Corporation | Nick Gattuccio,Microsoft Corporation
目录
使用事件的实践示例
示例 1:广播通知
示例 2:拆分项目
示例 3:跟踪文档源
示例 4:取消和重定向
示例 5:日志记录事件
结论
其他资源
本文是在 SharePoint Foundation 2010 中使用事件接收器(第 1 部分,共 2 部分)的延续。
使用事件的实践示例
既然您已深刻理解 Microsoft SharePoint Foundation 2010 中的事件模型,您可以开始在自己的代码中生成事件处理。本文其余部分演示通过各种方式使用 SharePoint 事件模型的五个示例:
广播通知 添加到网站集中任何数以百计的网站中的通知全部显示在根网站上的通知 Web 部件中。
拆分项目 当某个项目添加到列表时,该项目自动拆分成两个或两个以上的项目。此示例还演示在没有错误的情况下取消事件,以及在 Before 事件接收器中使用属性包。
跟踪文档源 编辑文档库中的文档时,会更新元数据属性。此示例演示属性升级和属性降级。
取消和重定向 取消用户操作时,用户重定向到错误页。
日志记录事件 网站集中发生的每个事件都记录为存储在网站上的列表中的列表项。
对于每个示例,您都可以下载一个 Microsoft Visual Studio 解决方案:
为了帮助您导航这些示例,我们提供了下面的 SharePoint 事件模型概述。表 3 按范围列出所有 SharePoint Foundation 2010 事件接收器,并指定它们所属的类以及它们可以与之关联的事件宿主。
表 3. 按范围分组的事件接收器
范围 |
方法 |
类 |
宿主 |
---|---|---|---|
网站集 |
|
|
|
Web |
|
|
|
列表 |
|
|
|
列表 |
|
|
|
字段 |
|
|
|
项目 |
|
|
|
工作流 |
|
|
示例 1:广播通知
在此示例中,您有一个包含数以百计单独网站的网站集;在根网站上,您有一个通知 Web 部件。此方案的目标是,让在任何单独网站上发布的所有通知都显示在放置在网站集根网站上的通知 Web 部件中。
下载代码示例:SharePoint 2010:使用事件接收器,示例 1:广播公告(该链接可能指向英文页面)
设计
假定网站集的根网站有一个"通知"列表,我们首先要做的是将此列表的 Web 部件添加到根网站的主页中。
添加通知 Web 部件
导航到网站集的根网站的主页。
单击"页面"选项卡,然后单击"编辑网页"。
单击"插入"选项卡,然后单击"现有列表"。您将看到根网站上的所有列表和文档库。
选择"通知"列表并单击"添加"。
接下来,您需要将已在任何子网站上发布的所有通知复制到根网站上的"通知"列表中。为此,创建一个 ItemAdded 事件接收器,用来将已添加到某列表(基于"通知"列表模板)中的任何项目复制到根网站上的"通知"列表中。对于位于网站集中的任何网站上的每个通知列表(根网站的"通知"列表例外),应触发您的事件逻辑。
实现
在 Microsoft Visual Studio 2010 中,基于 SharePoint 2010 事件接收器项目模板创建一个项目。选择在沙盒中部署您的解决方案。在 SharePoint 自定义向导中,选择"通知"列表作为事件源,选择 ItemAdded 作为要覆盖的事件,如图 6 所示。
图 6. SharePoint 自定义向导
此操作在具有 <Receivers> 元素的 Web 范围新建一个功能,该元素用于将 ItemAdded 事件接收器绑定到"通知"列表模板 (ListTemplateId="104"),如下面的摘要所示。
<Receivers ListTemplateId="104">
<Receiver>
<Name>EventReceiver1ItemAdded</Name>
<Type>ItemAdded</Type>
<Assembly>$SharePoint.Project.AssemblyFullName$</Assembly>
<Class>BroadcastAnn.EventReceiver1.EventReceiver1</Class>
<SequenceNumber>10000</SequenceNumber>
</Receiver>
</Receivers>
表 4 解释 <Receiver> 元素内的各种值。
表 4. Receiver 元素的各个元素
元素 |
说明 |
此示例中的值 |
---|---|---|
Name |
事件接收器绑定的名称。 |
EventReceiver1ItemAdded。随便提供您自己的名称。 |
Type |
事件的类型,它可以是 SPEventType 枚举的任何值的文本表示形式。 |
ItemAdded。 |
Assembly |
包含事件接收器代码的 DLL 的完全程序集名称。 |
$SharePoint.Project.AssemblyFullName$。当 Visual Studio 2010 生成解决方案时,此名称转换为完全程序集名称。 |
Class |
继承自 SPEventReceiverBase 并包含事件接收器代码的命名空间和类(格式为命名空间.类)。 |
BroadcastAnn.EventReceiver1.EventReceiver1,其中"BroadcastAnn.EventReceiver1"是命名空间,"EventReceiver1"是类名称。 |
SequenceNumber |
事件的执行顺序。例如,如果两个 ItemAdding 事件绑定到同一个事件宿主,则序号较小的事件先触发。 |
10000。基本上是默认值。 |
Synchronization |
无论事件是同步还是异步。After 事件为异步。 |
由于我们未指定同步,而 ItemAdded 为 After 事件,因此默认模式为异步。 |
若要使在网站集中的所有网站上触发事件,则必须将功能范围更改为网站;这样,当我们在网站集激活功能时将启用相应功能,而不必在每个子网站上激活功能。请注意,功能的网站范围指的是网站集(参见图 7);Web 范围指的是网站。
图 7. 设置功能范围
接下来,您必须打开 EventReceiver1.cs 文件,并向 ItemAdded 事件添加代码。结果是将已添加到子网站上的"通知"列表的项目复制到根网站的"通知"列表。此代码还追加到发布通知的人员的名称标题,并追加到发布通知的子网站的 URL 主体。
public override void ItemAdded(SPItemEventProperties properties)
{
// Get a reference to the site collection.
SPSite site = properties.OpenSite();
// Get a reference to the current site.
SPWeb currentWeb = properties.OpenWeb();
// Get a reference to the root site.
SPWeb rootWeb = site.RootWeb;
// Skip if the root web is the same as the current web.
if (rootWeb.Url == currentWeb.Url)
{
return;
}
// Get the current list.
SPList currentList = properties.List;
// Get the announcement list on the root site.
SPList rootList = rootWeb.Lists["Announcements"];
// Get the list item that was added.
SPListItem currentListItem = properties.ListItem;
// Add the announcement item to the list on the root web.
SPListItem rootListItem = rootList.Items.Add();
foreach (SPField field in currentList.Fields)
{
if (!field.ReadOnlyField)
{
rootListItem[field.Id] = currentListItem[field.Id];
}
}
// Append the user display name to the title.
rootListItem["Title"] += " - " + properties.UserDisplayName;
// Append the web URL to the body.
rootListItem["Body"] += string.Format("This announcements was made by {0} on subweb {1}",
properties.UserLoginName, properties.WebUrl);
rootListItem.Update();
}
现在,您可以生成解决方案并启动调试程序。此过程将创建您的解决方案,将它部署到 SharePoint 网站集,并对网站集的主页启动浏览器。确保您的网站集包含"通知"列表。
最后,基于工作组网站模板新建一个子网站。向"通知"列表添加一个项目,并确认该项目的副本显示在根网站上的"通知"列表中,如图 8 所示。
图 8. 添加到根网站上的"通知"列表的项目
示例 2:拆分项目
在此示例中,目标是向列表中添加一个项目,并使该项目拆分成两个新项目。例如,当用户添加一个 Xbox 360 Bundle,您可能希望阻止添加该项目,而是向列表中添加两个其他项目:"Xbox 360"和"Kinect"。更好的示例方案是,上载 .zip 文件,然后提取该文件的内容,并且其每个文件添加为单独的项目。
为了支持项目拆分方案,此示例还演示了如何在没有出错的情况下对 Before 事件接收器取消操作。
下载代码示例:SharePoint 2010:使用事件接收器,示例 2:拆分项目(该链接可能指向英文页面)
设计
此项目需要三个内容类型来分别表示"Xbox 360 Bundle"、"Xbox 360"和"Kinect"这三个项目。在此方案中,我们只需要项目的标题,因此每个内容类型都从"Items"内容类型继承,而无需添加任何额外字段。
添加基于"Xbox 360 Bundle"内容类型的新项目时,我们需要删除该项目,或取消添加它,然后基于"Xbox 360"和"Kinect"内容类型创建两个其他项目。
为此,我们必须先将 ItemAdding Before 事件绑定到"Xbox 360 Bundle"内容类型。ItemAdding 事件创建两个其他项目,并取消添加当前项目。由于我们不希望在添加"Xbox 360"或"Kinect"项目时触发任何逻辑,因此我们没有将这些内容类型绑定到任何事件接收器。为了让事情变得简单,我们没有处理更新;即,项目的内容类型从"Xbox 360"更改为"Xbox 360 Bundle"或"Kinect"时。
在取消添加"Xbox 360 Bundle"内容类型时,我们不希望接收到错误消息。因为我们访问的项目正在 Before 事件(更改应用于内容数据库之前)中进行添加,所以 properties.ListItem 属性仍为 null。若要访问项目字段,我们需要使用 properties.AfterProperties 属性包;这允许我们基于列表架构中字段的内部名称访问要复制的数据(在此示例中仅为标题)。
实现
基于空 SharePoint 项目模板创建一个 Visual Studio 2010 项目。选择在沙盒中部署您的解决方案。右键单击该项目,并单击"添加新项"。选择"内容类型",将项目命名为 Xbox360Bundle,然后单击"添加"。Visual Studio 将创建名称为"项目名称 - Xbox360Bundle"的内容类型。
重要说明 |
---|
请务必在 Element.xml 文件中将名称更改为简单的 Xbox360Bundle。 |
针对其他两个内容类型重复这些步骤,并应用名称 Xbox360 和 Kinect。
接下来,右键单击该项目,并添加名称为 BundleEventReceiver 的新事件接收器项目。选择"正在添加项"并单击"完成"。此步骤将创建一个覆盖 ItemAdding 事件的事件接收器类。但在默认情况下,它还将事件绑定到列表模板。我们希望将事件绑定到 Xbox360Bundle 内容类型而不是列表模板。因此,我们必须删除事件绑定 Elements.xml(如图 9 中选择所示),并在 Xbox360Bundle 内容类型 Element.xml 内部添加绑定。
图 9. Elements.xml 绑定
在我们添加事件接收器绑定之前,验证 Elements.xml 中内容类型的 Inherits 属性是否设置为 FALSE 而非 TRUE;否则,内容类型上的事件接收器绑定将不起作用。因为我们不再继承,所以我们需要自己定义所有字段引用。因此,我们必须添加对 Title 字段的字段引用。Xbox360Bundle 内容类型 Element.xml 应类似于以下形式。
<Elements xmlns="https://schemas.microsoft.com/sharepoint/">
<!-- Parent ContentType: Item (0x01) -->
<ContentType ID="0x01004497ac28eb9a47fbabee43f48c3f5973"
Name="Xbox360Bundle"
Group="Custom Content Types"
Description="My Content Type"
Inherits="FALSE"
Version="0">
<FieldRefs>
<FieldRef ID="{fa564e0f-0c70-4ab9-b863-0177e6ddd247}"
Name="Title"
Required="TRUE"
ShowInNewForm="TRUE"
ShowInEditForm="TRUE"/>
</FieldRefs>
<XmlDocuments>
<XmlDocument NamespaceURI="https://schemas.microsoft.com/sharepoint/events">
<spe:Receivers xmlns:spe="https://schemas.microsoft.com/sharepoint/events">
<Receiver>
<Name>Item Adding Event</Name>
<Type>ItemAdding</Type>
<Assembly>$SharePoint.Project.AssemblyFullName$</Assembly>
<Class>ItemSplitting.BundleEventReceiver.BundleEventReceiver</Class>
<SequenceNumber>10000</SequenceNumber>
</Receiver>
</spe:Receivers>
</XmlDocument>
</XmlDocuments>
</ContentType>
</Elements>
现在,让我们转向文件 BundleEventReceiver.cs,并向 ItemAdding 事件添加代码,该事件取消添加 Xbox360Bundle 项目并基于 Xbox360 和 Kinect 内容类型创建两个新项目。
public override void ItemAdding(SPItemEventProperties properties)
{
// Get a reference to the current list.
SPList list = properties.List;
// Get the "Xbox360" content type.
SPContentType xboxContentType = list.ContentTypes["XBox360"];
// Get the "Kinect" content type.
SPContentType kinectContentType = list.ContentTypes["Kinect"];
// If any of the content types are null, they were not created.
if (xboxContentType == null || kinectContentType == null)
{
properties.Status = SPEventReceiverStatus.CancelWithError;
properties.ErrorMessage = "The Xbox360 and Kinect content types must be present.";
return;
}
// Disable event firing so that ItemAdding is not called recursively.
this.EventFiringEnabled = false;
// Create the "Xbox360" item.
SPListItem xboxItem = list.AddItem();
xboxItem["Title"] = properties.AfterProperties["Title"] + " (Xbox 360)";
xboxItem["ContentTypeId"] = xboxContentType.Id;
xboxItem.Update();
// Create the "Kinect" item.
SPListItem kinectItem = list.AddItem();
kinectItem["Title"] = properties.AfterProperties["Title"] + " (Kinect)";
kinectItem["ContentTypeId"] = kinectContentType.Id;
kinectItem.Update();
// Re-enable event firing.
this.EventFiringEnabled = true;
// Cancel the creation of the "Xbox360Bundle" item but don't throw an error.
properties.Status = SPEventReceiverStatus.CancelNoError;
}
请注意以下有关前面代码的重要详细信息:
如果任何内容类型未绑定到列表,则我们将取消 ItemAdding 事件,并显示错误消息"Xbox360 和 Kinect 内容类型必须存在"。请注意,不需要添加语句 properties.Cancel = true。
为了避免 ItemAdding 以递归方式调用自己的情况(最高为调用堆栈中的限制),我们禁用事件触发之后,再将项目添加到 ItemAdding 事件中。在添加所有项目之后,重新启用事件触发。
我们使用表达式 properties.AfterProperties["field"],而不是 properties.ListITem["field"],因为 ItemAdding 是 Before 事件,这意味着还未创建列表项,并且 properties.ListItem 为 null。
我们不希望添加正在拆分的项目。因此,我们通过使用语句 properties.Status = SPEventReceiverStatus.CancelNoError 取消此事件,且没有引发错误。不要添加 properties.Cancel=true;这样做会覆盖行为,因而会引发错误。
现在,您已准备好生成解决方案并启动调试程序。这样做会创建您的解决方案,将它部署到 SharePoint 网站集,并对网站集的主页启动浏览器。
下一步是创建新列表,并将三个内容类型绑定到该列表。首先,确保允许将内容类型添加到该列表。通过在列表的高级设置页中将"允许管理内容类型"设置为"是",可以完成此操作。然后添加三个内容类型,如图 10 所示。
图 10. 绑定内容类型
现在,我们可以基于 Xbox360Bundle 内容类型,在列表上创建一个新项目,如图 11 所示。
图 11. 创建新项目
当您单击"保存"时,注意到该项目未添加。而是添加了两个其他项目:Xbox360 和 Kinect。如果我们从列表中删除 Xbox360 或 Kinect 内容类型,则您在试图保存时将收到错误消息。
示例 3:跟踪文档源
能够使用事件接收器操作文档属性是一项强大功能。此示例演示如何通过实现事件接收器的属性升级和降级执行此操作。通过使用属性升级和降级,在将文档添加到文档库时,甚至在编辑文档库中的文档时,您可以直接从事件接收器代码读取、添加或更新文档元数据。
在此示例中,我们编辑文档时,将 Source 元数据属性的值设置为文档库标题。
下载代码示例:SharePoint 2010:使用事件接收器,示例 3:跟踪文档源(该链接可能指向英文页面)
设计
在此示例中,我们创建一个绑定到网站中的所有文档库的 ItemUpdating 事件接收器。将文档保存到文档库时,事件处理程序代码将用文档库标题的值添加或更新 Source 元数据属性。
为了测试此行为,我们创建两个文档库并向每个文档库添加一个 Source 字段。然后,我们将 Microsoft Word 文档上载到第一个文档库并保存它。然后,下载此 Word 文档,使用 Word 客户端检查其元数据,并将此文档上载到另一个文档库。上载时,Source 字段的值应仍等于第一次将它上载到的文档库的标题。但是,当我们将此文档保存到新文档库时,其 Source 属性值应更改为新文档库的标题。我们检查新保存文档的元数据以确认此预期值。
实现
以下代码实现 ItemUpdating 事件。
public override void ItemUpdating(SPItemEventProperties properties)
{
string fieldInternalName = properties.List.Fields["Source"].InternalName;
properties.AfterProperties[fieldInternalName] = properties.ListTitle;
}
生成解决方案并启动调试程序。这些操作将执行创建解决方案、将它部署到 SharePoint 网站集、然后对网站集主页启动浏览器需要的所有工作。
创建一个新文档库,并向其添加 Source 字段。若要添加此字段,请转到文档库设置,单击"从现有网站栏添加",选择 Source 字段,单击"添加",然后单击"确定"。
接下来,将 Word 文档上载到该文档库。请注意,Source 字段的值为空。但是,单击"保存"时,Source 字段的值将更改为您的文档库的标题。
在本地计算机下载该文档的一个副本。创建一个新的文档库,并添加 Source 字段。将该文档上载到此新文档库。上载时,请注意 Source 字段的值仍然等于上次该文档保存到的文档库的标题。
现在单击 Save。Source 字段的值会更改为新文档库的标题。
示例 4:取消和重定向
此示例基于以下方案:取消用户操作(出错或出于其他原因),并且用户重定向到自定义错误页。
下载代码示例:SharePoint 2010:使用事件接收器,示例 4:取消和重定向(该链接可能指向英文页面)
设计
在此示例中,我们创建一个 Web 事件接收器,并向 WebMoving 事件添加代码,该事件取消其余网站 URL 的操作,并将用户重定向到"网站设置"页。(在此示例中,为简单起见 我们使用"网站设置"页。您可以将用户重定向到自定义错误消息页。)
实现
基于 SharePoint 2010 事件接收器项目模板创建一个 Visual Studio 2010 项目。选择在沙盒中部署您的解决方案。在自定义向导中,选择"Web 事件"作为事件源,选择"正在移动网站"作为事件。将功能的范围更改为网站。
添加以下代码,该代码将取消重命名事件并将用户重定向到"网站设置"页。
public override void WebMoving(SPWebEventProperties properties)
{
properties.Status = SPEventReceiverStatus.CancelWithRedirectUrl;
properties.RedirectUrl = properties.Web.ServerRelativeUrl +
"/_layouts/settings.aspx";
}
生成解决方案并启动调试程序。这将执行创建解决方案、将它部署到 SharePoint 网站集、然后对网站集主页启动浏览器需要的所有工作。
接下来,创建一个子网站并尝试重命名它。(我们必须创建一个新子网站,因为我们不能重命名根网站。)若要重命名该网站,请转到"网站设置"页。在"外观"部分,单击"标题、说明和图标"。在"URL 名称"框中,输入新网站名称。单击"确定"。
尝试重命名网站失败,并且您重定向到"网站设置"页。
示例 5:日志记录事件
此示例探究以下方案:事件日志记录代码捕获网站集上发生的每个事件,并将每个事件记录为网站上维护的列表上的一项。
下载代码示例:SharePoint 2010:使用事件接收器,示例 5:记录事件(该链接可能指向英文页面)
设计
表 5 列出所有 SharePoint 事件,并标识每个事件记录到的列表。我们未记录 SiteDeleting 或 SiteDeleted 事件,因为在删除网站集时会触发这两个事件。
表 5. 事件和日志
事件 |
类 |
日志列表 |
---|---|---|
SiteDeleting、SiteDeleted |
SPWebEventReceiver |
[未记录] |
WebAdding、WebProvisioned、WebDeleting、WebDeleted、WebMoving、WebMoved |
SPWebEventReceiver |
WebEventLogger |
ListAdding、ListAdded、ListDeleting、ListDeleted |
SPListEventReceiver |
ListEventLogger |
EmailReceived |
SPEmailEventReceiver |
ListEventLogger |
FieldAdding、FieldAdded、FieldUpdating、FieldUpdated、FieldDeleting、FieldDeleted |
SPListEventReceiver |
ListEventLogger |
ItemAdding、ItemAdded、ItemUpdating、ItemUpdated、ItemDeleting、ItemDeleted、ItemCheckingIn、ItemCheckedIn、ItemCheckingOut、ItemCheckedOut、ItemFileMoving、ItemFileMoved、ItemFileConverted、ItemAttachmentAdding、ItemAttachmentAdded、ItemAttachmentDeleting、ItemAttachmentDeleted |
SPItemEventReceiver |
ListItemEventLogger |
WorkflowStarting、WorkflowStarted、WorkflowCompleted、WorkflowPostponed |
SPWorkflowEventReceiver |
WorkflowEventLogger |
在此示例中,我们覆盖从 SPEventReceiverBase 继承的所有类的所有事件方法。我们还在 Common.cs 中创建一个包含帮助程序方法的其他类。然后,我们以同步形式绑定将 After 事件,以便对应列表在用户操作完成之前得到更新。利用功能,事件绑定范围可限定在网站级别,以便它处理网站集中所有网站上触发的所有事件。
实现
基于空 SharePoint 项目模板创建一个 Visual Studio 2010 项目。选择在沙盒中部署您的解决方案。
对于表 6 中的每个事件类型,右键单击该项目,单击"添加",指向"新项目",然后单击"事件接收器"。选择表中列出的事件属性,然后单击"完成"。
表 6. 新事件接收器的属性
事件类型 |
项目 |
事件 |
---|---|---|
Web |
无 |
所有事件 |
列表 |
无 |
所有事件 |
列表项 |
自定义列表 |
上下文事件除外的所有事件 |
列表电子邮件 |
自定义列表 |
所有事件 |
列表工作流 |
自定义列表 |
所有事件 |
若要处理在任意列表(不只是自定义列表)上触发的所有列表项事件、电子邮件事件和工作流事件,则从 <Receivers> 元素中删除 ListTemplateId 属性。(将 <Receivers ListTemplateId="100"> 更改为简单的 <Receivers>。)
默认情况下,After 事件是异步的。我们的所有 After 事件都需要是同步的,因此在每个 Elements.xml 文件中的每个 <Receiver> 元素下,添加 <Synchronization> 元素并将其值设置为"Synchronous"。例如,WebDeleted 事件应类似于以下形式。
<Receiver>
<Name>EventReceiver1WebDeleted</Name>
<Type>WebDeleted</Type>
<Assembly>$SharePoint.Project.AssemblyFullName$</Assembly>
<Class>PTCEventLogger.EventReceiver1.EventReceiver1</Class>
<SequenceNumber>10000</SequenceNumber>
<Synchronization>Synchronous</Synchronization>
</Receiver>
将功能范围更改为网站,以处理在整个网站集内触发的事件。请注意,创建新子网站意味着针对网站创建以及添加到网站的所有其他项目触发事件。这可能会消耗沙盒解决方案中的太多资源,因而导致 Web 创建失败。通过在项目属性中将"沙盒解决方案"更改为"False",或通过将功能范围更改回 Web,可以解决此潜在问题。
接下来,将一个名为 common 的类添加到项目中,并将它放到一个名为 Common.cs 的文件中。向此类添加以下三个帮助程序方法。
IsBeforeEvent 指示事件是 Before 事件还是 After 事件。
/// <summary> /// Indicates whether an event is a Before event or an After event. /// </summary> /// <param name="eventType"></param> /// <returns></returns> private static bool IsBeforeEvent(SPEventReceiverType eventType) { return (eventType < SPEventReceiverType.ItemAdded) ? true : false; }
EnsureLogList 确定日志列表是否已经存在。如果存在,则获取对其的引用。否则,创建该列表,并返回对其的引用。
/// <summary> /// Ensures that the Logs list with the specified name is created. /// </summary> /// <param name="web"></param> /// <param name="listName"></param> /// <returns></returns> private static SPList EnsureLogList(SPWeb web, string listName) { SPList list = null; try { list = web.Lists[listName]; } catch { // Create list. Guid listGuid = web.Lists.Add(listName, listName, SPListTemplateType.GenericList); list = web.Lists[listGuid]; list.OnQuickLaunch = true; // Add the fields to the list. // No need to add "Title" because it is added by default. // We use it to set the event name. list.Fields.Add("Event", SPFieldType.Text, true); list.Fields.Add("Before", SPFieldType.Boolean, true); list.Fields.Add("Date", SPFieldType.DateTime, true); list.Fields.Add("Details", SPFieldType.Note, false); // Specify which fields to view. SPView view = list.DefaultView; view.ViewFields.Add("Event"); view.ViewFields.Add("Before"); view.ViewFields.Add("Date"); view.ViewFields.Add("Details"); view.Update(); list.Update(); } return list; }
LogEvent 在指定列表上创建一个新项目,并将事件详细信息记录到该项目。
/// <summary> /// Log the event to the specified list. /// </summary> /// <param name="web"></param> /// <param name="listName"></param> /// <param name="eventType"></param> /// <param name="details"></param> public static void LogEvent(SPWeb web, string listName, SPEventReceiverType eventType, string details) { SPList logList = Common.EnsureLogList(web, listName); SPListItem logItem = logList.Items.Add(); logItem["Title"] = string.Format("{0} triggered at {1}", eventType, DateTime.Now); logItem["Event"] = eventType.ToString(); logItem["Before"] = Common.IsBeforeEvent(eventType); logItem["Date"] = DateTime.Now; logItem["Details"] = details; logItem.Update(); }
对于从 SPWebEventReceiver、SPListEventReceiver、SPEmailEventReceiver、SPItemEventReceiver 或 SPWorkflowEventReceiver 继承的每个类,必须创建一个 Log 方法来处理事件属性到对应列表的记录。还必须修改所有覆盖方法以调用这些 Log 方法。下面的代码列表演示如何对每个事件接收器执行此操作。
SPWebEventReceiver 更改所有覆盖事件。下面是如何更改这些事件之一的示例。
public override void WebProvisioned(SPWebEventProperties properties) { this.LogWebEventProperties(properties); }
添加 Log 方法,如下面的代码所示。
private void LogWebEventProperties(SPWebEventProperties properties) { // Specify the log list name. string listName = "WebEventLogger"; // Create string builder object. StringBuilder sb = new StringBuilder(); // Add properties that do not throw exceptions. sb.AppendFormat("Cancel: {0}\n", properties.Cancel); sb.AppendFormat("ErrorMessage: {0}\n", properties.ErrorMessage); sb.AppendFormat("EventType: {0}\n", properties.EventType); sb.AppendFormat("FullUrl: {0}\n", properties.FullUrl); sb.AppendFormat("NewServerRelativeUrl: {0}\n", properties.NewServerRelativeUrl); sb.AppendFormat("ParentWebId: {0}\n", properties.ParentWebId); sb.AppendFormat("ReceiverData: {0}\n", properties.ReceiverData); sb.AppendFormat("RedirectUrl: {0}\n", properties.RedirectUrl); sb.AppendFormat("ServerRelativeUrl: {0}\n", properties.ServerRelativeUrl); sb.AppendFormat("SiteId: {0}\n", properties.SiteId); sb.AppendFormat("Status: {0}\n", properties.Status); sb.AppendFormat("UserDisplayName: {0}\n", properties.UserDisplayName); sb.AppendFormat("UserLoginName: {0}\n", properties.UserLoginName); sb.AppendFormat("WebId: {0}\n", properties.WebId); sb.AppendFormat("Web: {0}\n", properties.Web); // Log the event to the list. this.EventFiringEnabled = false; Common.LogEvent(properties.Web, listName, properties.EventType, sb.ToString()); this.EventFiringEnabled = true; }
SPListEventReceiver 更改所有覆盖事件。下面是如何更改这些事件之一的示例。
public override void FieldAdded(SPListEventProperties properties) { this.LogListEventProperties(properties); }
添加 Log 方法,如下面的代码所示。
private void LogListEventProperties(SPListEventProperties properties) { // Specify the log list name. string listName = "ListEventLogger"; // Create the string builder object. StringBuilder sb = new StringBuilder(); // Add properties that do not throw exceptions. sb.AppendFormat("Cancel: {0}\n", properties.Cancel); sb.AppendFormat("ErrorMessage: {0}\n", properties.ErrorMessage); sb.AppendFormat("EventType: {0}\n", properties.EventType); sb.AppendFormat("FeatureId: {0}\n", properties.FeatureId); sb.AppendFormat("FieldName: {0}\n", properties.FieldName); sb.AppendFormat("FieldXml: {0}\n", properties.FieldXml); sb.AppendFormat("ListId: {0}\n", properties.ListId); sb.AppendFormat("ListTitle: {0}\n", properties.ListTitle); sb.AppendFormat("ReceiverData: {0}\n", properties.ReceiverData); sb.AppendFormat("RedirectUrl: {0}\n", properties.RedirectUrl); sb.AppendFormat("SiteId: {0}\n", properties.SiteId); sb.AppendFormat("Status: {0}\n", properties.Status); sb.AppendFormat("TemplateId: {0}\n", properties.TemplateId); sb.AppendFormat("UserDisplayName: {0}\n", properties.UserDisplayName); sb.AppendFormat("UserLoginName: {0}\n", properties.UserLoginName); sb.AppendFormat("WebId: {0}\n", properties.WebId); sb.AppendFormat("WebUrl: {0}\n", properties.WebUrl); sb.AppendFormat("Web: {0}\n", properties.Web); sb.AppendFormat("List: {0}\n", properties.List); // Add properties that might throw exceptions. try { sb.AppendFormat("Field: {0}\n", properties.Field); } catch (Exception e) { sb.AppendFormat("\nError accessing properties.Field:\n\n {0}", e); } // Log the event to the list. this.EventFiringEnabled = false; Common.LogEvent(properties.Web, listName, properties.EventType, sb.ToString()); this.EventFiringEnabled = true; }
SPEmailEventReceiver 如下所示更改 EmailReceived 事件。
public override void EmailReceived(SPList list, SPEmailMessage emailMessage, String receiverData) { // Specify the log list name. string listName = "ListEmailEventLogger"; // Create the string builder object. StringBuilder sb = new StringBuilder(); // Add the email message properties. sb.AppendFormat("From:\t {0}\n", emailMessage.Sender); sb.AppendFormat("To:\t {0}\n", emailMessage.Headers["To"]); sb.AppendFormat("Subject:\t {0}\n", emailMessage.Headers["Subject"]); sb.AppendFormat("Body:\t {0}\n", emailMessage.PlainTextBody); // Log the event to the list. Common.LogEvent(list.ParentWeb, listName, SPEventReceiverType.EmailReceived, sb.ToString()); }
SPItemEventReceiver 更改所有覆盖事件。下面是如何更改这些事件之一的示例。
public override void ItemAttachmentAdded(SPItemEventProperties properties) { this.LogItemEventProperties(properties); } Add the log method. private void LogItemEventProperties(SPItemEventProperties properties) { // Specify the log list name. string listName = "ListItemEventLogger"; // Create the string builder object. StringBuilder sb = new StringBuilder(); // Add properties that do not throw exceptions. sb.AppendFormat("AfterUrl: {0}\n", properties.AfterUrl); sb.AppendFormat("BeforeUrl: {0}\n", properties.BeforeUrl); sb.AppendFormat("Cancel: {0}\n", properties.Cancel); sb.AppendFormat("CurrentUserId: {0}\n", properties.CurrentUserId); sb.AppendFormat("ErrorMessage: {0}\n", properties.ErrorMessage); sb.AppendFormat("EventType: {0}\n", properties.EventType); sb.AppendFormat("ListId: {0}\n", properties.ListId); sb.AppendFormat("ListItemId: {0}\n", properties.ListItemId); sb.AppendFormat("ListTitle: {0}\n", properties.ListTitle); sb.AppendFormat("ReceiverData: {0}\n", properties.ReceiverData); sb.AppendFormat("RedirectUrl: {0}\n", properties.RedirectUrl); sb.AppendFormat("RelativeWebUrl: {0}\n", properties.RelativeWebUrl); sb.AppendFormat("SiteId: {0}\n", properties.SiteId); sb.AppendFormat("Status: {0}\n", properties.Status); sb.AppendFormat("UserDisplayName: {0}\n", properties.UserDisplayName); sb.AppendFormat("UserLoginName: {0}\n", properties.UserLoginName); sb.AppendFormat("Versionless: {0}\n", properties.Versionless); sb.AppendFormat("WebUrl: {0}\n", properties.WebUrl); sb.AppendFormat("Web: {0}\n", properties.Web); sb.AppendFormat("Zone: {0}\n", properties.Zone); sb.AppendFormat("Context: {0}\n", properties.Context); // Add properties that might throw exceptions. try { sb.AppendFormat("ListItem: {0}\n", properties.ListItem); } catch (Exception e) { sb.AppendFormat("\nError accessing properties.ListItem:\n\n {0}", e); } // Add AfterProperties property bag. sb.AppendFormat("AfterProperties: {0}\n", properties.AfterProperties); IEnumerator afterProperties = properties.AfterProperties.GetEnumerator(); int i = 0; while (afterProperties.MoveNext()) { DictionaryEntry entry = (DictionaryEntry)afterProperties.Current; sb.AppendFormat("[{0}]: ({1}, {2})\n", i++, entry.Key, entry.Value); } sb.AppendFormat("AfterProperties.ChangedProperties: {0}\n", properties.AfterProperties.ChangedProperties); IEnumerator changedAfterProperties = properties.AfterProperties.ChangedProperties.GetEnumerator(); i = 0; while (changedAfterProperties.MoveNext()) { DictionaryEntry entry = (DictionaryEntry)changedAfterProperties.Current; sb.AppendFormat("[{0}]: ({1}, {2})\n", i++, entry.Key, entry.Value); } // Add BeforeProperties property bag. sb.AppendFormat("BeforeProperties: {0}\n", properties.BeforeProperties); IEnumerator beforeProperties = properties.BeforeProperties.GetEnumerator(); i = 0; while (beforeProperties.MoveNext()) { DictionaryEntry entry = (DictionaryEntry)beforeProperties.Current; sb.AppendFormat("[{0}]: ({1}, {2})\n", i++, entry.Key, entry.Value); } sb.AppendFormat("BeforeProperties.ChangedProperties: {0}\n", properties.BeforeProperties.ChangedProperties); IEnumerator changedBeforeProperties = properties.BeforeProperties.ChangedProperties.GetEnumerator(); i = 0; while (changedBeforeProperties.MoveNext()) { DictionaryEntry entry = (DictionaryEntry)changedBeforeProperties.Current; sb.AppendFormat("[{0}]: ({1}, {2})\n", i++, entry.Key, entry.Value); } // Log the event to the list. this.EventFiringEnabled = false; Common.LogEvent(properties.Web, listName, properties.EventType, sb.ToString()); this.EventFiringEnabled = true; }
SPWorkflowEventReceiver 更改所有覆盖事件。下面是如何更改这些事件之一的示例。
public override void WorkflowStarting(SPWorkflowEventProperties properties) { this.LogWorkflowEventProperties(properties); }
添加 Log 方法,如下面的代码所示。
private void LogWorkflowEventProperties(SPWorkflowEventProperties properties) { // Specify the log list name. string listName = "WorkflowEventLogger"; // Create the string builder object. StringBuilder sb = new StringBuilder(); // Add properties that do not throw exceptions. sb.AppendFormat("AssociationData: {0}\n", properties.AssociationData); sb.AppendFormat("Cancel: {0}\n", properties.Cancel); sb.AppendFormat("CompletionType: {0}\n", properties.CompletionType); sb.AppendFormat("ErrorMessage: {0}\n", properties.ErrorMessage); sb.AppendFormat("EventType: {0}\n", properties.EventType); sb.AppendFormat("InitiationData: {0}\n", properties.InitiationData); sb.AppendFormat("InstanceId: {0}\n", properties.InstanceId); sb.AppendFormat("PostponedEvent: {0}\n", properties.PostponedEvent); sb.AppendFormat("ReceiverData: {0}\n", properties.ReceiverData); sb.AppendFormat("RedirectUrl: {0}\n", properties.RedirectUrl); sb.AppendFormat("RelativeWebUrl: {0}\n", properties.RelativeWebUrl); sb.AppendFormat("SiteId: {0}\n", properties.SiteId); sb.AppendFormat("Status: {0}\n", properties.Status); sb.AppendFormat("TerminatedByUserId: {0}\n", properties.TerminatedByUserId); sb.AppendFormat("WebUrl: {0}\n", properties.WebUrl); // Get activation properties. SPWorkflowActivationProperties activationProperties = properties.ActivationProperties; if (activationProperties != null) { sb.AppendFormat("ActivationProperties.AssociationData: {0}\n", activationProperties.AssociationData); sb.AppendFormat("ActivationProperties.HistoryListId: {0}\n", activationProperties.HistoryListId); sb.AppendFormat("ActivationProperties.HistoryListUrl: {0}\n", activationProperties.HistoryListUrl); sb.AppendFormat("ActivationProperties.InitiationData: {0}\n", activationProperties.InitiationData); sb.AppendFormat("ActivationProperties.ItemId: {0}\n", activationProperties.ItemId); sb.AppendFormat("ActivationProperties.ItemUrl: {0}\n", activationProperties.ItemUrl); sb.AppendFormat("ActivationProperties.ListId: {0}\n", activationProperties.ListId); sb.AppendFormat("ActivationProperties.ListUrl: {0}\n", activationProperties.ListUrl); sb.AppendFormat("ActivationProperties.Originator: {0}\n", activationProperties.Originator); sb.AppendFormat("ActivationProperties.OriginatorEmail: {0}\n", activationProperties.OriginatorEmail); sb.AppendFormat("ActivationProperties.SiteId: {0}\n", activationProperties.SiteId); sb.AppendFormat("ActivationProperties.SiteUrl: {0}\n", activationProperties.SiteUrl); sb.AppendFormat("ActivationProperties.TaskListId: {0}\n", activationProperties.TaskListId); sb.AppendFormat("ActivationProperties.TaskListUrl: {0}\n", activationProperties.TaskListUrl); sb.AppendFormat("ActivationProperties.TemplateName: {0}\n", activationProperties.TemplateName); sb.AppendFormat("ActivationProperties.WebId: {0}\n", activationProperties.WebId); sb.AppendFormat("ActivationProperties.WebUrl: {0}\n", activationProperties.WebUrl); sb.AppendFormat("ActivationProperties.WorkflowId: {0}\n", activationProperties.WorkflowId); // Add properties that might throw exceptions. try { sb.AppendFormat("ActivationProperties.Context: {0}\n", activationProperties.Context); sb.AppendFormat("ActivationProperties.HistoryList: {0}\n", activationProperties.HistoryList); sb.AppendFormat("ActivationProperties.Item: {0}\n", activationProperties.Item); sb.AppendFormat("ActivationProperties.List: {0}\n", activationProperties.List); sb.AppendFormat("ActivationProperties.OriginatorUser: {0}\n", activationProperties.OriginatorUser); sb.AppendFormat("ActivationProperties.Site: {0}\n", activationProperties.Site); sb.AppendFormat("ActivationProperties.TaskList: {0}\n", activationProperties.TaskList); sb.AppendFormat("ActivationProperties.Web: {0}\n", activationProperties.Web); sb.AppendFormat("ActivationProperties.Workflow: {0}\n", activationProperties.Workflow); } catch (Exception e) { sb.AppendFormat("\nError accessing ActivationProperties property:\n\n {0}", e); } } else { sb.AppendFormat("ActivationProperties is null\n"); } // Log the event to the list. this.EventFiringEnabled = false; Common.LogEvent(properties.ActivationProperties.Web, listName, properties.EventType, sb.ToString()); this.EventFiringEnabled = true; }
结论
现在,您了解了整个 SharePoint 2010 事件模型,如何创建事件接收器以及如何部署实现事件接收器的项目。(您还了解通过使用 Visual Studio 2010 中的事件接收器项目模板,可以节省多少工作。)而且,您有若干实践示例可适应于自己的需求。如果您甚至需要关于 SharePoint 事件模型的更多详细信息,请深入研究 SharePoint 2010 SDK,从 SharePoint Foundation 2010 中的事件开始。
其他资源
有关详细信息,请参阅以下资源: