Opinion wanted: Intercepting changes to lists/collections

bindinglist, c#, domain-driven-design, observablecollection

Solution

I would suggest creating something that parallels the `ObservableCollection<T>` where appropriate. Specifically, I would suggest following the existing techniques for notification of change of collection. Something like:

class MyObservableCollection<T> 
    : INotifyPropertyChanging,   // Already exists
      INotifyPropertyChanged,    // Already exists
      INotifyCollectionChanging, // You'll have to create this (based on INotifyCollectionChanged)
      INotifyCollectionChanged   // Already exists
{ }

This will follow established patterns so that clients are already familiar with the exposed interfaces-- three of the interfaces already exist. The use of existing interfaces will also allow more proper interaction with other already existing .NET technologies, such as WPF (which binds against the `INotifyPropertyChanged` and `INotifyCollectionChanged` interfaces.)

I would expect the `INotifyCollectionChanged` interface to look something like:

public interface INotifyCollectionChanged
{
    event CollectionChangingEventHandler CollectionChanging;
}

public delegate void CollectionChangingEventHandler(
    object source, 
    CollectionChangingEventArgs e
);

/// <remarks>  This should parallel CollectionChangedEventArgs.  the same
/// information should be passed to that event. </remarks>
public class CollectionChangingEventArgs : EventArgs
{
    // appropriate .ctors here

    public NotifyCollectionChangedAction Action { get; private set; }

    public IList NewItems { get; private set; }

    public int NewStartingIndex { get; private set; }

    public IList OldItems { get; private set; }

    public int OldStartingIndex { get; private set; }
}

If you wish to add cancellation support, simply add a writable `bool Cancel` property to `CollectionChangingEventArgs` that the collection will read to determine whether to execute the change that's about to occur.

I suppose this falls under your Option 2. This is the way to go because, to interoperate properly with other .net technologies that monitor changing collections, you're going to have to implement it anyway for `INotifyCollectionChanged`. This will definitely follow the policy of "Least Surprise" in your interface.

Problem

Although `BindingList<T>` and `ObservableCollection<T>` provide mechanisms to detect list changes, they don't support mechanisms to detect/intercept changes before they happen. I'm writing a couple of interfaces to support this, but I want to canvas your opinion. Option 1: Lists raise events for each type of action Here, consumers might write code like this: ``` public class Order : Entity { public Order() { this.OrderItems = new List<OrderItem>(); this.OrderItems.InsertingItem += new ListChangingEventHandler<OrderItem>(OrderItems_InsertingItem); this.OrderItems.SettingItem += new ListChangingEventHandler<OrderItem>(OrderItems_SettingItem); this.OrderItems.RemovingItem += new ListChangingEventHandler<OrderItem>(OrderItems_RemovingItem); } virtual public List<OrderItem> OrderItems { get; internal set; } void OrderItems_InsertingItem(object sender, IOperationEventArgs<OrderItem> e) { if (!validationPasses) { e.Cancel = true; return; } e.Item.Parent = this; } void OrderItems_SettingItem(object sender, IOperationEventArgs<OrderItem> e) { if (!validationPasses) { e.Cancel = true; return; } e.Item.Parent = this; } void OrderItems_RemovingItem(object sender, IOperationEventArgs<OrderItem> e) { if (!validationPasses) { e.Cancel = true; return; } e.Item.Parent = null; } } ``` Option 2: Lists raise a single event, and the action is determined from the event args Here, consumers might write code like this: ``` public class Order : Entity { public Order() { this.OrderItems = new List<OrderItem>(); this.OrderItems.ListChanging += new ListChangingEventHandler<OrderItem>(OrderItems_ListChanging); } virtual public List<OrderItem> OrderItems { get; internal set; } void OrderItems_ListChanging(object sender, IOperationEventArgs<OrderItem> e) { switch (e.Action) { case ListChangingType.Inserting: case ListChangingType.Setting: if (validationPasses) { e.Item.Parent = this; } else { e.Cancel = true; } break; case ListChangingType.Removing: if (validationPasses) { e.Item.Parent = null; } else { e.Cancel = true; } break; } } } ``` Background: I'm writing a set of general purpose interfaces/classes that represent the core components of DDD, and I'm making the source code available (hence the need to create friendly interfaces). This question is about making the interface as cohesive as possible, so that consumers can derive and implement their own collections without losing the core semantics. PS: Please don't suggest using `AddXYZ()` and `RemoveXYZ()` methods for each list, because I've already discounted that idea. PPS: I must include developers using .NET 2.0 :) Related question.

Original source

Related problems