Cancelling modifications - csf-dev/CSF.Collections.EventRaising GitHub Wiki

A feature of the Before events (BeforeAdd, BeforeRemove and for collection wrappers, BeforeReplace) is the ability to cancel the action entirely.

Cancelling an addition, removal or replacement is exactly that, the collection is left unmodified. It is as if Add, Remove or whatever method was used to modify state or replace the collection was never called. Other subscribers to the before events may see that this has happened by observing the IsCancelled property on the event args. If an action is cancelled in this way then the corresponding after events are never triggered.

Remember that .NET does not guarantee the order in which event subscribers are notified - so if there are three listeners, then the first listener might be informed with IsCancelled == false, the second listener chooses to call Cancel() and the third listener sees the event args with IsCancelled == true. But there is no promise that things will occur in that order.

The downsides of this mechanism

Using events to cancel an action could be thought of as "just enough rope with which to hang oneself". Despite the attractiveness of being able to apply business logic to determine whether an addition/removal/replacement may go ahead, and have this silently occur when the only API exposed to client code is ICollection<T>, I would warn you against it.

It would be a very confusing state of affairs for developers of client code if the addition/removal to/from a collection did not actually result in what they asked-for. The contract of ICollection<T> (plus IList<T> and ISet<T>) states that methods such as Add and Remove do indeed add/remove items respectively. Changing their behaviour by silently aborting would be breaking that contract.

If your business logic indicates that it is not permitted to modify a collection in the requested manner then it might be more appropriate to raise an exception from the event handler (as unsavoury as that may be).

In this scenario though, it is almost certainly more correct to ensure that attempted modifications to the collection only occur via a business logic service with its own API. This service would expose methods with signatures such as bool TryAdd or bool TryRemove which make it clear that they do not guarantee that the requested modification has taken place, and may also report upon their success/failure.

In short - event-raising collections are not a silver bullet and are not intended as a solution to complex business logic cases in which you should have been using a service type all along.