Problem : SPItemEventReceiver’s ItemUpdating and ItemUpdated methods are called twice! (to be precise)
And if you have Update() or SystemUpdate() call on the document / item, this leads to getting an exception message on screen about “XYZ has already been modified”. This happens due to race condition between the two threads when updating the document / item.
Background
In SharePoint, an event receiver can be hooked to a SPList to listen to any events occuring on the SPList.
This is how you do it :
SPWeb spWeb = GetSPWebInstance(); // assuming that you have a document library by name of "My List" SPList sharedDocuments = web.Lists["My List"]; // attach a ItemUpdated event. SPEventReceiverDefinition spEventReceiverDef = sharedDocuments.EventReceivers.Add(); spEventReceiverDef.Class = "Fully.Qualified.Class.Name"; spEventReceiverDef.Assembly = "AssemblyName, Version=x.x.x.x, Culture=neutral, PublicKeyToken=thePublicKeyToken" spEventReceiverDef.Sequence = 5000; // put any number here spEventReceiverDef.Type = SPEventReceiverType.ItemUpdated; spEventReceiverDef.Update();
To attach an ItemUpdating event receiver, all you need to do it replace the type from SPEventReceiverType.ItemUpdated to SPEventReceiverType.ItemUpdating, and ofcourse the class / assembly information if you have the implementation in a different class / assembly.
ItemUpdating gets called synchronously just before an item is updated. And ItemUpdated gets called after the item is updated.
Cause
When force checkout is enabled on document library, this is how the document editing works :
When you checkout a document for editing, a local copy of the document is created that saves all the changes made by the user. At the time of checking-in the document, this local copy updates the original copy (even if there are no changes), then there is another request that does check-in on the document.
This is the reason why ItemUpdating and ItemUpdated are called twice (once for each step of check-in process).
Solution
A Property : “vti_sourcecontrolcheckedoutby” is provided that can be used to distinguish if the update is because of check-in or due to other reasons.
If this property exist in BeforeProperties and not in AfterProperties of SPItemEventProperties instance then it means that the callback is for item check-in event, else it is for event other than item check-in.
So if you have a piece of code that you want to be executed only when the check-in is done then enclose it in a conditional statement like this :
public override void ItemUpdated(SPItemEventProperties properties)
{
// perform actions only if the update event is triggered by check-in
if (properties.AfterProperties["vti_sourcecontrolcheckedoutby"] == null &&
properties.BeforeProperties["vti_sourcecontrolcheckedoutby"] != null)
{
// your code goes here
}
else
{
//ignore
}
}
Putting your code block in correct conditional section would make sure that your code gets executed for the check-in event callback.
