深入理解SharePoint中的Event Receiver功能
来源:互联网 发布:战舰世界炮弹数据 编辑:程序博客网 时间:2024/05/07 08:25
原文地址(请自备云梯):点击打开链接
这篇博客讨论了SharePoint中Event Receiver的工作原理,并通过讨论,解决了一个上传文档的时候常见的问题。
我们知道Event Receivers分为两种,一种是同步的Event Receiver(Synchronous,例如ItemAdding与ItemUpdating),另外一种是异步的Event Receiver(Asynchronous,例如ItemAdded与ItemUpdated)。Event Receiver是在托管代码中实现的,因此使用SPRequest这个非托管代码来调用Event Receiver的时候,是使用ISPEventManager这个COM接口来调用的,这个接口定义在Microsoft.SharePoint.SPEventManager类中。
[ComImport, SuppressUnmanagedCodeSecurity, InterfaceType((short)1), Guid(“BDEADF0F-C265-11D0-BCED-00A0C90AB50F”)]public interface ISPEventManager{ [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] void ExecuteItemEventReceivers( ref byte[] userToken, ref objecteventReceivers, ref ItemEventReceiverParams itemEventParams, out objectchangedFields, outEventReceiverResult eventResult, out stringerrorMessage); [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] void EnqueueItemEventReceivers(ref byte[] userToken, ref objecteventReceivers, ref ItemEventReceiverParams itemEventParams); [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] void ExecuteListEventReceivers( ref byte[] userToken, ref objecteventReceivers, ref ListEventReceiverParams ListEventParams, outEventReceiverResult eventResult, out stringerrorMessage); [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] void EnqueueListEventReceivers(ref byte[] userToken, ref objecteventReceivers, ref ListEventReceiverParams ListEventParams); [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] void ExecuteWebEventReceivers( ref byte[] userToken, ref objecteventReceivers, ref WebEventReceiverParams webEventParams, outEventReceiverResult eventResult, out stringerrorMessage); [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] void EnqueueWebEventReceivers(ref byte[] userToken, ref objecteventReceivers, ref WebEventReceiverParams webEventParams);}
这个接口包含两种方法,一种是EnqueueXXXEventReceivers,包含EnqueueItemEventReceivers, EnqueueListEventReceivers和EnqueueWebEventReceivers,这类方法是用来处理异步的event receivers的。另一种是ExecuteXXXEventReceivers,包含ExecuteItemEventReceivers,ExecuteListEventReceivers和ExecuteWebEventReceivers这种方法是用来处理同步的event receivers的。
Event Receivers,无论是同步还是异步,都是运行在触发他们执行的进程中,例如,用户通过webpart向文档库中添加一个文档,那么event receiver会在w3wp进程中运行。如果用户通过一个winform程序MyWinformApp.exe上传文档,那么event receiver会在MyWinformApp.exe进程中运行。这里需要注意的是异步event receiver,有时候异步的event receiver需要一定时间来完成,如果使用MyWinformApp.exe上传文档之后立即关闭,但是这时候event receiver还没有执行完,那么event receiver会被中断,或者压根儿就没有执行。这种情况在w3wp进程中也是一样的,如果在event receivers运行的时候重启IIS,那么event reciever会被终止,除非使用iisreset /noforce命令,这样的话虽然会等待event receivers执行完成再重启iis,但是即使这样,如果你想在event receivers保存对item的修改,这些修改将会丢失。
所以,重启IIS(不使用/noforce)会中断所有event receivers的执行,而使用/noforce来重启iis,虽然不会中断event receivers的执行,但是对SharePoint对象的修改可能不会生效。因此,不要让你的event receiver的运行时间超过一秒钟,否则的话很可能会被中断,而导致整个应用程序的错误!
SharePoint会新起一个线程(来自System.Threading.ThreadPool)来执行异步的event receiver,每触发一个异步的event receiver,就会新起一个线程,并把这个线程添加到执行队列中。如果用户上传了100个文件,触发了100个event receivers,就会有100个线程被加入队列(即调用100次EnqueueItemEventReceivers)。
我们知道在线程之间切换是比较消耗资源的,如果同时有100个线程在运行,会降低进程的执行效率。我见过一个迁移工具,在文档库之间移动文件,移动的同时有一些event receivers被触发。移动几百个文件的时间也就一分钟左右,但是执行event receivers的时间却用了45分钟。90%时间都消耗在了线程之间的切换上,只有10%的时间真正用于执行代码,所以如果在移动文件之后,可以等待event receiver执行完成再移动写一个文件的话,执行时间会缩短,因为省去了线程的切换时间。
我们再研究一下event receiver的重要参数“SPItemEventProperties”,这个参数在触发event receiver的时候被创建,同时会被其他event receiver共享,例如在ItemAdded触发时创建的SPItemEventProperties参数,ItemUpdated也可以使用。需要注意的是,同步的event receivers共享一个SPItemEventProperties,而异步的event receivers共享另一个SPItemEventProperties参数。还有一个有趣的地方是,非托管的SPRequest类会保存在同步event receiver中对这个参数的修改,供之后执行的异步的event receiver使用,也就是可以使用这个参数在event receivers之间传递数据!但是这样做是有限制的,目前只能在ItemAdding和ItemUpdating这两个同步的event receivers中修改AfterProperties集合,然后这些数据可以在异步的ItemAdded和ItemUpdated的AfterProperties集合中使用。
另一个常见的问题是,我们在event receiver中使用SPItemEventProperties.OpenWeb()方法的时候,是否需要显示的释放web。如果我们查看SPItemEventProperties类的代码就会找到答案:
public sealed class SPItemEventProperties : SPEventPropertiesBase, IDisposable{ … private SPSite OpenSite() { if (((this.m_site == null) && (this.WebUrl != null)) && (this.m_site == null)) { if (this.m_userToken == null) { this.m_site = new SPSite(this.WebUrl); } else { this.m_site = new SPSite(this.WebUrl, this.m_userToken); } this.m_siteCreatedByThis = true; } return this.m_site; } … public SPWeb OpenWeb() { this.OpenSite(); if (this.m_site == null) { return null; } return this.m_site.OpenWeb(this.RelativeWebUrl); } … public void Dispose() { if (this.m_site != null) { while (this.m_siteCreatedByThis) { this.m_site.Dispose(); this.m_site = null; this.m_siteCreatedByThis = false; break; } } }}
从代码中可以看到,SPItemEventProperties自己已经实现了IDisposable和Dispose来释放资源,SPEventManager会在event receiver执行完成的时候自动调用dispose方法来释放资源,因此我们不需要显示的释放。
解决问题:在ItemAdded执行完之前弹出EditForm.aspx页面
就是在一个文档库上添加了ItemAdded事件,用来在上传文档的时候修改item的某个属性的值。因为ItemAdded事件是异步的,这样就可能导致该事件没有完成的时候,就弹出EditForm.aspx页面编辑属性,或者在编辑完属性的时候,点击OK无法保存,提示文件正在被修改。为了解决这个问题,我们需要写一些代码,来确保在ItemAdded执行完之后再弹出EditForm.aspx页面。一种方法是,修改EditForm.aspx页面,添加一个web control,用来等待ItemAdded事件的完成,同时我们需要一个特殊的ItemAdded事件来与这个web control一起工作。
这个特殊的ItemAdded事件代码可以从以下链接下载:
SharePointInternals.SynchronousItemAdded.dll
SharePointInternals.SynchronousItemAdded – Source Code
使用方法很简单,只需要创建一个继承SPSynchronousReceiver的event receiver同时重写ItemAddedSynchronously方法即可:
public class SPTestReceiver : SPSynchronousReceiver{ protected override void ItemAddedSynchronously(SPItemEventProperties properties) { // Your code goes here } // Your other item receiver overrides go here }然后在EditForm.aspx页面中插入WaitForItemAdded这个web control:
<%@Register TagPrefix=”SharePointInternals” Assembly=”SharePointInternals.SynchronousItemAdded, Version=1.0.0.0, Culture=neutral, PublicKeyToken=d7dbdc19a16aed51″ namespace=”SharePointInternals.WebControls”%> … <asp:Content ContentPlaceHolderId=”PlaceHolderMain” runat=”server”> <SharePointInternals:WaitForItemAdded ID=”waitForItemAdded1″ runat=”server”/> … </asp:Content>
只有将WaitForItemAdded添加到EditForm.aspx页面中才能解决问题,如果将WaitForItemAdded添加到ListFormWebPart使用的ListFormTemplate中,这个control确实会确保ItemAdded事件执行完成,但是这个时候,item早已经被加载SPContext中了,因为在加载EditForm.aspx的时候,会使用SPContext.Current.ListItem来加载item的属性,如果加载的时候ItemAdded并没有执行完,那么EditForm.aspx仍旧会显示旧的属性值,也就是ItemAdded事件执行之前的属性值,而不是ItemAdded事件中修改的新的属性值。这个时候如果点击EditForm.aspx上的OK按钮,会报错:
The file … has been modified by … on …
using (SPSite site = new SPSite(“http://server/sites/test”))using (SPWeb web = site.OpenWeb()){ SPList list = web.Lists[“Shared Documents”]; SPFile file = list.RootFolder.Files.Add(fileName, fileBytes); SPSynchronousReceiver.WaitForItemAddedReceivers(list, file.Item.ID);}
最后,有一个特殊的event receiver需要特别注意一下:ListItemFileConverted,这个event receiver是在执行SPFile.Convert()方法的时候初始化的,是在托管代码中初始化的,而不像其他的event receivers一样在非托管代码中初始化。这是个异步的event receiver。
总结一下今天的讨论:除了ListItemFileConverted这个event receiver之外,其他的receivers都是在非托管代码中初始化的。当SPRequest这个非托管对象调用了某些触发receivers的方法时,就开始初始化receivers,之后通过ISPEventManager COM 接口调用它们。我们不需要显示的释放properties.OpenWeb()方法返回的web对象,有时候new一个新的SPSite或者SPWeb会是更好的选择。所有异步的receivers都在线程中执行,如果线程过多,会因为频繁切换线程而降低SharePoint的性能。如果重启IIS,会中断event receivers的执行。最后我们使用代码解决了ItemAdded没有执行完就弹出EditForm.aspx页面的问题。
- 深入理解SharePoint中的Event Receiver功能
- SharePoint中的EventReceiver 之二 绑定Event Receiver到列表类型或列表实例
- 使用Visual Studio给SharePoint列表添加Event Receiver
- SharePoint Event Receiver 网站范围和绑定的列表范围
- 深入理解iOS开发中的BitCode功能
- 深入理解iOS开发中的BitCode功能
- 深入理解iOS开发中的BitCode功能
- 深入理解iOS开发中的BitCode功能
- 深入理解iOS开发中的BitCode功能
- 深入理解iOS开发中的BitCode功能
- 深入理解iOS开发中的BitCode功能
- 深入理解ios开发中的bitcode功能
- 深入理解iOS开发中的BitCode功能
- 深入理解Delegate与Event
- 深入理解nodejs Event loop
- SharePoint 2010部署WCF续 - feature event receiver实现自动部署
- How to redirect to a Custom Page for Event Receiver in SharePoint 2010
- Managing ItemUpdating and ItemUpdated Events Firing Twice in a SharePoint Item Event Receiver
- vi和vim的操作手册
- 主成分分析(PCA)原理及推导
- cocos2d-x学习经历:2.2.6创建项目
- Android 打包签名 怎样生成keystore到完成签名
- 初级 滤镜实现
- 深入理解SharePoint中的Event Receiver功能
- Linux下查看文件内容的命令
- 重新签名APK
- Find Minimum in Rotated Sorted Array II
- 教你一步步编写新闻客户端一
- nasm 汇编指令 列表
- 对于相机的情况说明
- Excel Sheet Column Number
- 关于tomcat重启脚本