# Dynamic Data: reactive collections so you stop hand-maintaining a list

> Dynamic Data is a .NET library that brings Reactive Extensions to collections, letting you maintain a source cache or source list and chain declarative operators to filter, transform, sort and bind the result without touching the target collection by hand. It sits in the ReactiveUI ecosystem and its operator set runs to at least sixty collection operations, but the change set model it is built on is not a drop-in for a plain ObservableCollection.

**reactivemarbles/DynamicData** — Reactive collections based on Rx.Net

- Repository: https://github.com/reactivemarbles/DynamicData
- Stars: 1,906 · Forks: 194
- Language: C#
- License: MIT
- Published: 2026-10-19 · Updated: 2026-10-19 · Language: en
- Canonical page: https://hysenlabs.com/projects/reactivemarbles-dynamicdata

## The problem: a collection that has to mirror another one

The README states the problem in plain terms. Rx is extremely powerful but out of the box provides nothing to assist with managing collections. In most applications there is a need to update the collections dynamically. Typically a collection is loaded and after the initial load, asynchronous updates are received, and the original collection will need to reflect these changes.

The simple case is simple, and then it is not. Typical applications are much more complicated and may apply a filter, transform the original dto and apply a sort. Even with these simple everyday operations the complexity of the code is quickly magnified.

That is the gap Dynamic Data fills. The README says it has been developed to remove the tedious code of dynamically maintaining collections, and that it has grown to become functionally very rich with at least 60 collection based operations which enable filtering, sorting, grouping, joining different sources, transforms, binding, pagination, data virtualisation, expiration, disposal management plus more.

The mental model is one line. You maintain a data source, either a SourceCache<TObject, TKey> or a SourceList<TObject>, then chain together various combinations of operators to declaratively manipulate and shape the data without the need to directly manage any collection.

That word declaratively is doing the work. The alternative to a chain of operators is a set of event handlers that keep a second list in sync with a first, and every time you add a filter or a sort to the pipeline you add another handler.

## The trading example, which is the whole idea in twelve lines

The example in the README is the clearest statement of the design, because it shows the target collection never being written to by hand.

```cs
ReadOnlyObservableCollection<TradeProxy> list;

var myTradeCache = new SourceCache<Trade, long>(trade => trade.Id);
var myOperation = myTradeCache.Connect() 
		.Filter(trade=>trade.Status == TradeStatus.Live) 
		.Transform(trade => new TradeProxy(trade))
		.Sort(SortExpressionComparer<TradeProxy>.Descending(t => t.Timestamp))
		.ObserveOnDispatcher()
		.Bind(out list) 
		.DisposeMany()
		.Subscribe()
```

Reading it left to right: connect to the cache, keep only live trades, wrap each in a proxy object, order by most recent first, move onto the dispatcher thread, bind the result to a ReadOnlyObservableCollection, dispose the proxies when they are no longer used, and subscribe.

The README's summary is that the magic is that as myTradeCache is maintained, the target observable collection looks after itself.

Two operators in that chain carry more weight than the rest and deserve attention.

ObserveOnDispatcher() is what makes the result safe to bind to a UI collection. The upstream data may arrive on a background thread, and the bind operation produces a collection that a WPF or Avalonia view binds to, so the notification has to be marshalled to the UI thread at that point rather than earlier.

DisposeMany() is the other one. Because Transform creates a TradeProxy for each trade, there is now a second object with its own lifetime, and without an explicit disposal chain the proxies would leak as trades come and go. The README points this out directly, noting that since the proxy is disposable, DisposeMany() will ensure the proxy is disposed when no longer used.

The comment about the resulting trade proxies being bound on the dispatcher thread and the collection looking after itself is the whole argument for the library in one sentence.

## SourceList, SourceCache, and the choice you have to get right

There are two source types, and the README has a section comparing them, which tells you the choice matters.

The observable list is created with new SourceList<int>(). It provides the direct edit methods you would expect, and each one produces a distinct change notification.

```cs
var myInts = new SourceList<int>();
myInts.AddRange(Enumerable.Range(0, 10000)); 
myInts.Add(99999); 
myInts.Remove(99999);
```

The batching mechanism is what makes that usable at volume. To increase efficiency when making multiple amendments, the list provides a means of batch editing, achieved using the .Edit method which ensures only a single change notification is produced.

```cs
myInts.Edit(innerList =>
{
   innerList.Clear();
   innerList.AddRange(Enumerable.Range(0, 10000));
});
```

That matters for a UI, because ten thousand separate change notifications against a bound collection is ten thousand layout passes.

Exposing the list publicly is handled by AsObservableList, which returns an IObservableList<int> and hides the edit methods.

```cs
IObservableList<int> readonlyInts = myInts.AsObservableList();
```

So the list is for things that have an inherent order and no identity beyond position, and the cache is for things that have a key. A SourceCache<Trade, long> is keyed by trade.Id, which is what lets you update or remove a specific trade without knowing where it sits in a list, and what lets different parts of an application refer to the same entity.

Picking the wrong one is the mistake that produces a collection with duplicate items, so the list versus cache section is the first thing to read.

## Change sets: the unit of work between source and view

Dynamic Data's contribution is not only the operators but the change set, and the README's table of contents shows how many ways there are to produce one.

There is a section on connecting to a cache or list, and then several on creating an observable change set from other sources: from an Rx observable, from an Rx observable with an expiring cache, from an ObservableCollection, and from a BindingList. There is also a section on using the ObservableChangeSet static class, and a section on consuming observable change sets.

The change set is the currency of the library. An Rx observable tells you that something happened. A change set tells you what happened to a collection, in a form that an operator can consume and transform, and a binding operation can apply to a UI collection.

That distinction is what the README's opening claim rests on. Rx is powerful but provides nothing for managing collections. Dynamic Data adds a layer where the unit of change is a structured description of collection mutations, which is what filtering, sorting and grouping need to work on.

The four creation routes matter practically. Converting an existing ObservableCollection or BindingList into a change set is the migration path for an application that already has a collection and wants the operators without rewriting the data layer. Wrapping a plain Rx observable gives you a source when the data arrives as a stream rather than a list. The expiring cache variant is for data with a natural time to live.

The final stage is consumption, and the README's ordering puts the change set before the operators, which is the reverse of how most people think about the library when they arrive at it.

## Nine and a half versions, and a SonarCloud dashboard

The release history shows 9.5.0 from 2026-10-13, 9.4.33 from 2026-06-30 and 9.4.31 from 2026-03-08, and the last push was on 2026-10-14, so the tree is being worked on.

The numbering is worth noting. A library at 9.x with patch releases numbered in the thirties is a mature project that has accumulated a lot of surface, and it also means the version number alone tells you little about what changed, so the ReleaseNotes.md file at the root is the document to read when upgrading.

The tooling is unusually thorough for a library. There is a build workflow badge, and the SonarCloud badges cover coverage, reliability rating, duplicated lines percentage, vulnerabilities and a security rating, all measured on new code. Running a security rating and a vulnerability count on new code is a stricter discipline than most libraries adopt.

There is a nuget.config at the root, a version.json file, a RELEASING.md document, and a .github/ directory. The README points to the ReactiveUI Slack community, and the project lives under the reactivemarbles organisation, which reflects that it is now maintained under the ReactiveUI umbrella.

The sample projects are worth a look too. The README links a WPF trading project, a collection of unit tested examples of many different operators, and Tail Blazer for tailing files. The trading project is the same scenario as the README example, so it shows the pattern at a larger scale than a snippet can.

## Conclusion

Dynamic Data suits a WPF or Avalonia desktop application that receives streaming updates and has to keep a filtered and sorted view on screen, because the operator chain means the view maintains itself and DisposeMany makes the lifetime of created objects explicit. It is the wrong tool for a simple list you populate once, since the change set machinery and the operator learning curve cost more than they save, and it is the wrong choice for a web backend where a reactive stream library already covers the case. Before you adopt it, read the observable list versus observable cache section, because choosing SourceList where a SourceCache fits is the mistake that produces duplicate items and key conflicts later.

## FAQ

### What is Dynamic Data and what problem does it solve?

It is a portable class library that brings the power of Reactive Extensions to collections. You maintain a SourceCache or SourceList and chain operators to filter, transform and sort the data declaratively, so the target observable collection maintains itself instead of being updated by hand.

### How do I create an observable list with Dynamic Data?

Instantiate a SourceList<int> and use the direct edit methods such as Add, AddRange and Remove, each of which produces a distinct change notification. For efficiency when making multiple amendments, use the .Edit method so that only a single change notification is produced.

### What is the difference between a SourceList and a SourceCache in Dynamic Data?

A SourceList is for items with an inherent order and no identity beyond position, while a SourceCache is keyed, as in new SourceCache<Trade, long>(trade => trade.Id), which lets you update or remove a specific item by key. The README has a dedicated section comparing the two.

### Why does the Dynamic Data example use ObserveOnDispatcher and DisposeMany?

ObserveOnDispatcher moves the change notifications onto the dispatcher thread so the result is safe to bind to a UI collection. DisposeMany disposes the proxy objects created by the Transform operator when they are no longer used, since each transformed item has its own lifetime.

### How do I hide the edit methods of a Dynamic Data list?

Call AsObservableList on the source list, which returns an IObservableList<int> and hides the edit methods so the collection can be exposed publicly.

## Sources

- [Issues](https://github.com/reactivemarbles/DynamicData/issues)
- [License: MIT](https://github.com/reactivemarbles/DynamicData/blob/main/LICENSE)
- [reactivemarbles/DynamicData on GitHub](https://github.com/reactivemarbles/DynamicData)
- [README](https://github.com/reactivemarbles/DynamicData/blob/main/README.md)
- [Releases](https://github.com/reactivemarbles/DynamicData/releases)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/reactivemarbles-dynamicdata
