As you may know, in SharePoint there are many event receivers (EventReceiver) that allow you to invoke custom code when performing standard operations with SharePoint objects, such as adding / removing / changing list items. Working with event receivers deserves a separate article, but there are already quite a lot of them on this topic, for example,
here .
Today we will look at 2 particular cases of problems that a novice SharePoint developer may experience when working with receivers:
1. Cyclic call events.
It is quite easy to imagine a situation where the receiver can “drive” itself into an infinite loop. For example, take the event receiver for the item “After Update” (ItemUpdated). Your code performs additional actions on this element (for example, based on the data entered by the user, perform additional calculations and write them into the required field), after which, of course, call Update to save the data. After the call, what should happen? Of course. Your code will be recalled, which was described in the receiver. And so endlessly.
')
public override void ItemUpdated(SPItemEventProperties properties) { base.ItemUpdated(properties); SPListItem Item = properties.ListItem; Item["Title"] = DateTime.Today.ToString("dd.MM.yyyy"); Item.Update(); }
For reasons unknown to me, some developers believe that to disable the execution of event handlers, it is enough to call
SystemUpdate instead of
Update on the object. But it is not. Not only does
msdn say that this method allows you to update list items without updating the ModifiedBy, ModifiedBy, and increasing the item version, and the opposite is proved experimentally.
That is, using
SystemUpdate to disable calling event handlers will not be enough, although, I confess, in the above example, it is better to call
SystemUpdate instead of
Update to exclude changes to the above fields and create a new version of the element if versioning is enabled.
This problem will help us solve the
EventFiringEnabled property of
SPEventReceiverBase (or even of the current inherited object from
SPEventReceiverBase ).
This property allows you to control the ability to call any event handlers inherited from the
SPEventReceiverBase class in the current thread. That is, in your code you can turn off and on calls to event handlers and not be afraid that other code or simple user work (which also calls receivers) will be overshadowed by disabling or disabling handlers in your particular method.
public override void ItemUpdated(SPItemEventProperties properties) { base.ItemUpdated(properties); SPListItem Item = properties.ListItem; this.EventFiringEnabled = false; Item["Title"] = DateTime.Today.ToString("dd.MM.yyyy"); Item.Update(); this.EventFiringEnabled = true; }
When calling the specified code when the method is executed, the call to receivers will be disabled. It is worth paying attention to the fact that all receivers inherited from
SPEventReceiverBase , if you set the
EventFiringEnabled property to
false, will be disabled and you will need to take care of this yourself. That is, if in the code between turning off the call to the receivers and turning on you try to update another list to which the event handler is attached to the update, it will not be executed (Why? About this below). The code that was supposed to be executed must be executed by force.
If you pay attention to the above code, it will immediately become clear that it is not optimal. At least it is more logical to wrap it in try-catch-finally (well, or at least just try-finally). You know what
it is or
is it ?
The result is the following code, which, regardless of the output, will turn the execution of event handlers back
public override void ItemUpdated(SPItemEventProperties properties) { base.ItemUpdated(properties); try { SPListItem Item = properties.ListItem; this.EventFiringEnabled = false; Item["Title"] = DateTime.Today.ToString("dd.MM.yyyy"); Item.Update(); } finally { this.EventFiringEnabled = true; } }
Below, another possible disconnection option will be presented with the
using instruction.
2. The need to disable events when performing certain operations outside the receiver.
How to disable the execution of the receiver inside the receiver we figured out. But very much part of the need to turn off the execution of receivers outside the receiver. That is, you, for example, in the console application want to update the list item, but do not want to call the receiver, which will process the data in it and overwrite it, perform the necessary manipulations with the element and then update it with the receiver.
At its core, the task is very similar to the previous one, but we do not have the
SPEventReceiverBase object and there is nothing to disable the call to the receivers. Here it is necessary to turn to the refler and see what this class is and how to proceed.
protected bool EventFiringEnabled { get { return !SPEventManager.EventFiringDisabled; } set { SPEventManager.EventFiringDisabled = !value; } }
From the code it is clear that the class has the
EventFiringEnabled property (we used it a little earlier to turn off the event call) and with its help it gets or sets the value from the static property
SPEventManager.EventFiringDisabled . The code for this property is shown below:
internal static bool EventFiringDisabled { get { SPEventManager.EnsureTlsEventFiringDisabled(); object data = Thread.GetData(SPEventManager.m_tlsEventFiringDisabled); return data != null && (bool) data; } set { SPEventManager.EnsureTlsEventFiringDisabled(); Thread.SetData(SPEventManager.m_tlsEventFiringDisabled, (object) (bool) (value ? 1 : 0)); } }
Since the method is static and the inner work is already based on threads, it turns out that every class inherited from
SPEventReceiverBase does not quite work with the context of the current object. The required information is read and recorded in the memory cells allocated for our threads. It turns out that it is not so much important from which receiver the disconnection or inclusion of calls to other handlers is performed, how much in which flow it is done. Thus, it is enough to create our own class inherited from
SPEventReceiverBase (or from
SPItemEventReceiver for our particular case, which in turn is also a successor), to initialize the instance in the required place of the code and work with the
EventFiringEnabled property already familiar to us.
public class DisableItemEvents : SPItemEventReceiver { public bool CustomEventFiringEnabled { get { return base.EventFiringEnabled; } set { base.EventFiringEnabled = value; } } }
And call, for example, as follows:
var EventsDisable = new DisableItemEvents(); try { Item["Title"] = DateTime.Today.ToString("dd.MM.yyyy"); EventsDisable.CustomEventFiringEnabled = false; Item.Update(); } finally { EventsDisable.CustomEventFiringEnabled = true; }
It turned out what we expected. When updating, the receiver did not work. And as I said earlier, this approach has the right to life, and many people use it like this, but I would describe this construction a little differently. I really like using patterns. At least for the fact that using this functionality, you can be sure that regardless of the errors inside the instructions, Dispose will always be called.
I described my class as follows:
public class DisableItemEvents : SPItemEventReceiver, IDisposable { private bool _EventStatus; public DisableItemEvents() { _EventStatus = base.EventFiringEnabled; base.EventFiringEnabled = false; } public void Dispose() { base.EventFiringEnabled = _EventStatus; } }
Working with this class is obtained as follows:
using (new DisableItemEvents()) { Item.Update();
Here, comments are superfluous, but nonetheless. When the
DisableItemEvents object is initialized, the original value of the
EventFiringEnabled property will be saved and set to false. And when the object is
displaced, the EventFiringEnabled property will be returned back.
Thus, we considered possible options for disabling calls to event handlers both inside and outside the receivers.