SharePoint Online–Remote Event Receiver (RER)–ItemUpdated/ItemAdded thread race

 

SharePoint Online Event handlers are deployed as Remote Event Receiver (RER), which uses the client-side object model (CSOM).  Hence, the typical even race that happens when you have a code logic to update some property in ItemAdded/ItemUpdated will recursively trigger the ItemUpdated event.  Since, it’s CSOM, the server side event disabling is not available.  The only workaround to resolve this issue is to have conditional checks when ItemAdded/ItemUpdated event occurs.  The most robust way I’ve seen it works is to programmatically disassociate and re-associate the event receivers when it’s not required to be fired.

The remote event receivers are mostly deployed as WCF service where it could be handled correctly.  Below is the code snippet that I’ve used which works to resolve the event receiver issue:

SPRemoteEventResult IRemoteEventService.ProcessEvent(SPRemoteEventProperties properties)
{
SPRemoteEventResult result = new SPRemoteEventResult();
switch (properties.EventType)
{
case SPRemoteEventType.ItemAdded:
try
{
ManageUpdateEventHandler(properties.ItemEventProperties.WebUrl, false);
HandleItemAdded(properties);
}
finally
{
ManageUpdateEventHandler(properties.ItemEventProperties.WebUrl, true);
}
break;
case SPRemoteEventType.ItemUpdated:
try
{
ManageUpdateEventHandler(properties.ItemEventProperties.WebUrl, false);
HandleItemUpdated(properties);
}
finally
{
ManageUpdateEventHandler(properties.ItemEventProperties.WebUrl, true);
}
break;
case SPRemoteEventType.ItemCheckedIn:
HandleItemCheckedin(properties);
break;
}
return result;
}

private void ManageUpdateEventHandler(string webUrl, bool enable)
{
Guid eventReceiverId = Guid.Empty ;
using(ClientContext context = CreateClientContextUsingAdminToken(webUrl))
{
if(context!=null)
{
List sourceList= context.Web.Lists.GetByTitle("<List Title>");
context.Load(sourceList, a => a.EventReceivers);
context.ExecuteQuery();

            for (int c = 0; c < sourceList.EventReceivers.Count; c++)
{
if (sourceList.EventReceivers[c].ReceiverName.Equals("<receiver name>"))
{
eventReceiverId = sourceList.EventReceivers[c].ReceiverId;
break;
}
}

            if(enable)
{
if(eventReceiverId==Guid.Empty)
{
EventReceiverDefinitionCreationInformation itemUpdatedEvent = new EventReceiverDefinitionCreationInformation();
itemUpdatedEvent .EventType = EventReceiverType.ItemUpdated;
itemUpdatedEvent .ReceiverUrl = ConfigurationManager.AppSettings.Get("RemoteEventReceiverURL");
itemUpdatedEvent .ReceiverName = "<receiver name>";
itemUpdatedEvent .Synchronization = EventReceiverSynchronization.Synchronous;

                    sourceList.EventReceivers.Add(itemUpdatedEvent );
context.ExecuteQuery();
}
}
else
{
if(eventReceiverId!=Guid.Empty)
{
sourceList.EventReceivers.GetById(eventReceiverId).DeleteObject();
sourceList.Update();
context.ExecuteQuery();
}
}
}
}
}