Original address (please bring your own ladder): Click to open the link
This blog discusses how event receiver works in SharePoint and, through discussion, solves a common problem when uploading a document.
We know that the event receivers is divided into two kinds, one is synchronous event Receiver (synchronous, for example itemadding and itemupdating), the other is asynchronous event Receiver (asynchronous, such as ItemAdded and itemupdated). Event receiver is implemented in managed code, so when invoking event receiver using SPRequest, this unmanaged code is invoked using the COM interface of Ispeventmanager. This interface is defined in the Microsoft.SharePoint.SPEventManager class.
[ComImport, SuppressUnmanagedCodeSecurity, InterfaceType ((short) 1), Guid ("bdeadf0f-c265-11d0-bced-00a0c90ab50f")] public interface Ispeventmanager {[MethodImpl (methodimploptions.internalcall, methodcodetype = Methodcodetype.runtim e)] void executeitemeventreceivers (ref byte[] usertoken, ref objecteventreceivers, ref Itemeventreceiverparam
S 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 ListEventPa
Rams, 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, OUTEVENTRECEI
Verresult Eventresult, out stringerrormessage); [MethodImpl (Methodimploptions.internalcall, methodcodetype = methodcodetype.runtime)] void EnqueueWebEventReceivers
(ref byte[] usertoken, ref objecteventreceivers, ref webeventreceiverparams webeventparams); }
This interface consists of two methods, one is Enqueuexxxeventreceivers, which contains enqueueitemeventreceivers, Enqueuelisteventreceivers and Enqueuewebeventreceivers, such methods are used to handle asynchronous event receivers. The other is Executexxxeventreceivers, which contains executeitemeventreceivers, Executelisteventreceivers and Executewebeventreceivers This method is used to handle synchronous event receivers.
Event receivers, whether synchronous or asynchronous, runs in a process that triggers their execution, for example, when a user adds a document to a document library through a WebPart, the event receiver runs in the w3wp process. If a user uploads a document through a WinForm program MyWinformApp.exe, the event receiver runs in the MyWinformApp.exe process. It is important to note that asynchronous event receiver, sometimes asynchronous event receiver, takes a certain amount of time to complete, if it is closed immediately after uploading the document using MyWinformApp.exe, but the event receiver is not finished, Then the event receiver will be interrupted or not executed at all. This situation is the same in the w3wp process, if you restart IIS while the event receivers is running, the event reciever will be terminated unless you use the Iisreset/noforce command, which will wait for the event Receivers execution completes and then restarts IIS, but even then, if you want to save the change to item in event receivers, those changes will be lost.
Therefore, restarting IIS (without using/noforce) interrupts execution of all event receivers and uses/noforce to restart IIS, although the execution of event receivers is not interrupted, but modifications to SharePoint objects may not take effect. Therefore, do not let your event receiver run for more than a second, otherwise it is likely to be interrupted, resulting in an entire application error.
SharePoint will start a new thread (from System.Threading.ThreadPool) to execute the asynchronous event receiver, and each trigger an asynchronous event receiver will start a new thread, and add this thread to the execution queue. If the user uploads 100 files and triggers 100 event receivers, 100 threads will be queued (that is, 100 times enqueueitemeventreceivers).
We know that switching between threads is a relatively resource-intensive process, and that if there are 100 of them running at the same time, it will reduce the execution efficiency. I've seen a migration tool that moves files between document libraries, while some event receivers are triggered. It takes about a minute to move hundreds of files, but it takes 45 minutes to execute the event receivers. 90% time is spent on switching between threads, only 10% of the time is really used to execute code, so if after moving the file, you can wait for the event receiver to complete and then move to write a file, the execution time will be shortened, because the thread switching time is omitted.
We'll look at the important parameter "Spitemeventproperties" of event receiver, which is created when the event receiver is triggered and shared by other event receiver. For example, the Spitemeventproperties parameter created when the itemadded is triggered, itemupdated can also be used. It is important to note that the synchronous event receivers shares one spitemeventproperties, while the asynchronous event receivers shares another spitemeventproperties parameter. Another interesting point is that the unmanaged SPRequest class will be saved in the synchronization event receiver to modify this parameter for subsequent asynchronous event receiver use, that is, you can use this parameter in the event Pass data between receivers. However, this is limited, and currently only modifies the Afterproperties collection in the two synchronized event receivers of Itemadding and itemupdating. This data can then be used in the Afterproperties collection of asynchronous itemadded and itemupdated.
Another common question is whether we need to display the release web when we use the Spitemeventproperties.openweb () method in event receiver. If we look at the code for the Spitemeventproperties class, we'll find the answer:
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_sitecreat Edbythis) {This.m_site.
Dispose ();
This.m_site = null; This.m_sitecreatedbythis = false;
Break }
}
}
}
As you can see from the code, Spitemeventproperties has implemented IDisposable and dispose to release resources, Speventmanager will be in the event When receiver executes, the Dispose method is called automatically to release the resource, so we do not need to display the release.
Fix the problem: Pop up editform.aspx page before itemadded finishes executing
is to add the ItemAdded event to a document library to modify the value of an item's property when uploading a document. Because the ItemAdded event is asynchronous, this can cause the event to not complete when the EditForm.aspx page edit property pops up, or when the property is edited, click OK to not save, prompting the file is being modified.
To solve this problem, we need to write some code to make sure that the EditForm.aspx page pops up after ItemAdded executes. One way is to modify the EditForm.aspx page, add a Web control to wait for the ItemAdded event to complete, and we need a special itemadded event to work with this Web control.
This special ItemAdded event code can be downloaded from the following link:
SharePointInternals.SynchronousItemAdded.dll
Sharepointinternals.synchronousitemadded–source Code
Using the method is simple, just create an event receiver that inherits Spsynchronousreceiver and override the Itemaddedsynchronously method at the same time:
public class Sptestreceiver:spsynchronousreceiver
{
protected override void itemaddedsynchronously ( Spitemeventproperties properties)
{
//Your code goes here
}
//Your Other item receiver overrides go
}
Then insert waitforitemadded This Web control in the editform.aspx page:
<% @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>
The problem can only be resolved if you add waitforitemadded to the EditForm.aspx page, and if you add waitforitemadded to listformtemplate that Listformwebpart uses, the control does ensure that the item Added event execution completed, but this time, item has already been loaded in SPContext, Because when loading editform.aspx, the property of item is loaded with SPContext.Current.ListItem, and if itemadded is not executed when loading, then editform.aspx will still display the old property value, that is ItemAdd The Ed event executes prior to the property value, not the new property value that is modified in the ItemAdded event. This time if you click on the OK button on editform.aspx, you will get an error:
The file ... have been modified by ... on ...
We have previously discussed the problem of switching threads wasting time during the execution of multiple asynchronous event receivers, but can also be solved in the same way, that is, after each document is added, it is forced to wait for the event receiver to complete before adding the next file. This can be accomplished using the Spsynchronousreceiver.waitforitemaddedreceivers () method:
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);
}
Finally, there is a special event receiver that requires special attention: listitemfileconverted, this event receiver is initialized at the time of execution of the Spfile.convert () method, initialized in managed code, Instead of initializing in unmanaged code like any other event receivers. This is an asynchronous event receiver.
To summarize today's discussion: In addition to listitemfileconverted this event receiver, the other receivers are initialized in unmanaged code. When sprequest this unmanaged object invokes some of the methods that trigger receivers, it starts initializing receivers and then calls them through the Ispeventmanager COM interface. We do not need to display the release properties. The OpenWeb () method returns the Web object, and sometimes new SPSite or SPWeb will be a better choice. All asynchronous receivers are executed inline, and if there are too many threads, the performance of SharePoint is reduced because of frequent thread switching. If you restart IIS, the execution of event receivers will be interrupted. Finally, we used the code to solve the problem of itemadded the EditForm.aspx page without completing the execution.