Transaction support in an observable collection - c#

I'm interested the most efficient way to change an observable collection in such a way that only one property changed is fired. Lets say that I want to populate the list with 3 items, there is no addCollection method or something like that, so I have to do clear + 3 times add. Do I need to create a different observable collection and assign? Or what techniqies do others use?

NET Framework's ObservableCollection class sends individual notifications on as each item added to the collection and provides no mechanism for AddRange-type functionality. However you can very easily create your own collection that implements INotifyCollectionChanged and send whatever notifications you like.
On issue you may encounter is that the INotifyCollectionChanged interface includes the ability to specify that multiple items were added to the collection in a single message, but no standard NET Framework classes actually create these notifications. Because of this, some third-party and open source controls that assume only one item has been added when they receive an Add notification. Even the built-in NET Framework classes may have undiscovered bugs related to this.
For these reasons I would recommend your custom collection have a mode in which it can be set to always send a Reset notification at the end of an AddRange instead of a single multi-item Add notification. You could optimize this further by sending multiple single-item Add notifictions or a Reset notification depending on the actual number of items added.
Of course there are situations in which it is just as easy to replace the ObservableCollection with a new one. At times this will be much less efficient than looping Add() because event handlers and CollectionViews are rebuilt. Other times it will be more efficient if the collection is large and your loop only adds a few items at a time.
And sometimes it won't work at all.

Related

CollectionChanged and IList of Items - why the difficulties

I am looking into the topic why a ObservableCollection/ListCollectionView/CollectionView raises a NotSuportedException when calling the CollectionChanged with the parameter of IList.
//Throws an exception
private void collectionChanged_Removed(IList items)
{
if (CollectionChanged != null)
CollectionChanged(this, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, items));
}
I have found several Webpages, talking about this topic and they suggest either using the Reset ability to force a complete redraw of the UI, or just simply call for each item the CollectionChanged or some more creative way: http://geekswithblogs.net/NewThingsILearned/archive/2008/01/16/listcollectionviewcollectionview-doesnt-support-notifycollectionchanged-with-multiple-items.aspx
I just cant find the WHY?
For me it makes no sense why this would be the case.
Is there any chance that this lacking feature, which we all face at some point in our Development Cycle, since the Add method just has to much of an overhead when you want to Add multiple items fast, will be implemented any time (.Net 5, C# 6...).
Edit:
In my specific case, I have written my own class :
public class ObservableList<T> : IList<T>, IList, IEnumerable<T>,
INotifyCollectionChanged
{
public event NotifyCollectionChangedEventHandler CollectionChanged;
//other stuff...
}
And still throws the said NotSupportedException.
Inspired by VirtualBlackFox's answer I took a look under the hood of the CollectionView classes in ILSpy. It appears that the primary reason why the lack of support for Range operations is because internally the CollectionView uses a change log to centrally manage pending changes of all kinds and dispatch messages on a per/item basis.
By its very purpose, the CollectionView could store 1000s of records used simultaneously with multiple UI controls representing its underlying data. So, adding or deleting records must be done on an atomic basis to maintain the integrity of the UI controls that access the view information. You can't synchronize incremental changes with multiple UI subscribers using bulk change events without passing the grouping, sorting, and filtering functionality of the CollectionView onto the UI controls that use it.
The CollectionView also derives from System.Windows.Threading.Dispatcher so the issue may be co-related to how it manages work items on it's thread. The call path includes a protected ProcessCollectionChanged method that specifically processes individual changes on the UI thread. So, updating ranges may interfere with the whole threading model it uses to interact with UI elements that use it.
I totally agree that having consumers of the CollectionView pass in an IList to NotifyCollectionChangedEventArgs is silly. It specifically rejects anything with a length != 1 and hard-codes for args.NewItems[0] internally.
As #nmclean said in the comments the problem isn't in the collection emitting CollectionChanged but on the receiving end.
If you look at the code of ListCollectionView (Using DotPeek for example or on new versions of visual studio you can access the reference source code) you will notice that each time the attached collection change it call a method ValidateCollectionChangedEventArgs that throw when there is more than one element changed
private void ValidateCollectionChangedEventArgs(NotifyCollectionChangedEventArgs e)
{
switch (e.Action)
{
case NotifyCollectionChangedAction.Add:
if (e.NewItems.Count == 1)
break;
else
throw new NotSupportedException(System.Windows.SR.Get("RangeActionsNotSupported"));
...
The rest of the class and it's CollectionView base class are already big beasts (2710 and 2027 lines in the source published on the reference source code) so Microsoft might have wanted to avoid supporting a complex case that the collection that they recommend don't create anyway.
(The method handling collection change is already 141 lines in the reference source code and adding multi element support will make it grow even more or need a careful split and potentially break other things...)
I didn't find any suggestions linked to adding support for range events in Microsoft Connect but you should submit your own if it is important for you.
I guess this is mainly for performance reasons. I was also shocked when I saw that CollectionView also does not accept the -1 value for NewStartingIndex or OldStartingIndex. So basically, CollectionView always wants a mapping from items to their indices. However, it does not require this mapping to be exact (which is strange from my point of view), it is allowed that NewStartingIndex is smaller than the correct index (unless it is -1).
I think the root of the problem is that large parts within Microsoft still think that a list is the one and only way to implement a collection, which of course just is not true. Here, the creators of NotifyCollectionChangedEventArgs thought about alternatives (such as linked lists or hashing collections), but the UI guys did not. Or at least they did not want to support these collections as they appear rather rarely.
The temporary solution is useless. It only hides the problems.
The solution could lie in making events where you provide an entire new list to the observers. That way Microsoft won't have to implement efficient range handlers for each type of observer.

ObservableCollection or IEnumerable?

Which is faster ObservableCollection or IEnumerable to load a listbox?
I am currently using an IEnumerable as the ItemSource of the Listbox.
Will it have any impact on the performance if I change this to ObservableCollection, if it is even possible?
That really depends on which class is implementing the IEnumerable. IEnumerable is simply an interface and any number of classes could be behind the actual implementation. You don't really bind the IEnumerable, since that is an interface, but you are binding some collection/list that implements IEnumerable. ObservableCollection also implements IEnumerable so should be able to safely use that as the ItemSource instead.
Enumerating through a IEnumerable to fill a ListBox should never be a performance bottleneck in your application, unless you would be using some custom class that does a lot of logic when iterating through values, in which case your design would probably be flawed.
That being said, the best way is of course to simply measure. There is no substitute for measuring and making assumptions is always likely to come back to bite you later.
I'm quite new to WPF and C#, but what I have understood so far is that if you use an IEnumerable as ItemSource, such as a List, you won't be able to see changes in that List unless you replace it with a different one:
IEnumerable<int> myList = new List<int>();
myList.Add(3);
In this case (provided that you do all the necessary stuff to inform your GUI that myList is changed) nothing will happen. If you do something like this, instead:
myList = new List<int> { 100, 200, 300 };
your GUI will be informed.
If you use a ObservableCollection, instead, your GUI will be informed even when you add a new element with the Add method. So you have to decide whether you want to add elements to your ListBox or not.
If you bind to a collection that implements IEnumerable and that is not an ObservableCollection, for example a List, the binding target will not be notified of items added/removed.
Not using a ObservableCollection does only make sense if the number of items in the collection does not change. Only then it makes sense to compare ObservableCollection with other objects. If the number of items change, then you do not have any choice but using an ObservableCollection.
In addition, as already pointed out by other users, you do not have to compare ObservableCollection with IEnumerable, but with a class implementing IEnumerable.
As an example, a comparison ObservableCollection - List can be found in Stack Overflow question ObservableCollection<> vs. List<>.

Is there List type that can be Enumerated while it is Changing?

Sometimes it is useful to enumerate a list while it is changing.
e.g.
foreach (var item in listOfEntities)
item.Update();
// somewhere else (with someEntity contained in listOfEntities)
// an add or remove is made:
someEntity.OnUpdate += (s,e) => listOfEntities.Remove(someEntity);
This will fail if listOfEntities is a List<T>.
There are workarounds like making a copy or a simple for-loop, each with different drawbacks, but I would like to know if there is a list type in the framework (or open source) that supports this.
Look at the collections in System.Collections.Concurrent. There's no list there, but the collections' enumerators do "represents a moment-in-time snapshot of the contents of the [collection]".
These collections are designed for access from multiple threads, so they will be better suited to applications like the code sample you posted.
This has nothing to do with List<T>; it is a limitation of the enumerator. If you change the state of the collection underneath the enumerator it will throw, period.
You could use a for loop, but you will then run into logical errors as you index into a collection after the number of items have changed.
It's probably a bad idea to swap items in and out of a collection while you are enumerating it in another thread. I would stick with the tried and true method of recording the items to be removed in another collection or locking the collection while it is being enumerated.
I'm not claiming this is an impossible problem to solve, I just don't know of an easy way to do it.

How do you buffer items into groups in Reactive Extensions?

I have an IObservable; where a property change has an entity ID and PropertyName. I want to use this to update a database, but if multiple properties change almost simultaneously I only want to do one update for all properties of the same entity.
If this was a static IEnumerable and I was using LINQ I could simply use:
MyList.GroupBy(C=>C.EntityID);
However, the list never terminates (never calls IObserver.OnComplete). What I want to be able to do is wait a period of time, say 1 second, group all the calls appropriately for that one second.
Ideally I would have separate counters for each EntityID and they would reset whenever a new property change was found for that EntityID.
I can't use something like Throttle because I want to handle all of the property changes, I just want to handle them together in one go.
Here you go:
MyObservable
.Buffer(TimeSpan.FromSeconds(1.0))
.Select(MyList =>
MyList.GroupBy(C=>C.EntityID));
The Buffer method seems to do what you want. Give it the TimeSpan and it'll collapse all the messages into a list. There is also the Window method which does something similar, but I'm not entirely sure what its semantics might be.

How to process n items in a collection concurrently

i am building a class that inherits from List. Items are going to be added to this collection at runtime and what i want is to have this class automatically do something with each block of n items after they have been added.
So here is the scenario.
1] Create new class that inherits from List - CollectionX
2] At runtime we will be calling ColX.Add(T) many times
3] When ColX has 500 or more items it is to move them into a temporary area and do work on them, then delete them. Keeping in mind that all the while items will still be being added to ColX.
So i guess my question is how do i implement this nicely and by ensuring that it is thread safe.
The work that is to be performed must be done in blocks so i dont think a queue will work as you can only dequeue 1 item at a time.
I think im looking for more of a pattern than actual types or libraries.
Can anyone help?
Don't let CollectionX inherit from List.
Instead, use 2 Lists internally, Add() to 1 and process the other.
This way you only have to lock the swapping of the Lists. If there are timing problems you could even use a 3rd List to prevent blockage.

Categories

Resources