DynamicData
Reactive collections based on Rx.Net
Install / Use
/learn @reactivemarbles/DynamicDataREADME
<a href="https://reactiveui.net/slack">
<img src="https://img.shields.io/badge/chat-slack-blue.svg">
</a>
<br />
<br />
<a href="https://github.com/reactiveui/DynamicData">
<img width="170" height="170" src="https://github.com/reactiveui/styleguide/blob/master/logo_dynamic_data/logo.svg"/>
</a>
Dynamic Data
Dynamic Data is a portable class library which brings the power of Reactive Extensions (Rx) to collections.
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. The original collection will need to reflect these changes. In simple scenarios the code is simple. However, typical applications are much more complicated and may apply a filter, transform the original dto and apply a sort. Even with these simple every day operations the complexity of the code is quickly magnified. Dynamic data has been developed to remove the tedious code of dynamically maintaining collections. It has grown to become functionally very rich with at least 60 collection based operations which amongst other things enable filtering, sorting, grouping, joining different sources, transforms, binding, pagination, data virtualisation, expiration, disposal management plus more.
The concept behind using dynamic data is you maintain a data source (either SourceCache<TObject, TKey> or SourceList<TObject>), then chain together various combinations of operators to declaratively manipulate and shape the data without the need to directly manage any collection.
As an example the following code will filter trades to select only live trades, creates a proxy for each live trade, and finally orders the results by most recent first. The resulting trade proxies are bound on the dispatcher thread to an observable collection. Also since the proxy is disposable DisposeMany() will ensure the proxy is disposed when no longer used.
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()
The magic is that as myTradeCache is maintained the target observable collection looks after itself.
This is a simple example to show how using Dynamic Data's collections and operators make in-memory data management extremely easy and can reduce the size and complexity of your code base by abstracting complicated and often repetitive operations.
Sample Projects
- Sample WPF project trading project Dynamic Trader
- Various unit tested examples of many different operators Snippets
- Tail Blazer for tailing files
Get in touch
If you have any questions, want to get involved or would simply like to keep abreast of developments, you are welcome to join the slack community Reactive UI Slack.
Table of Contents
- Dynamic Data
- Table of Contents
- Create Dynamic Data Collections
- Creating Observable Change Sets
- Connect to a Cache or List
- Create an Observable Change Set from an Rx Observable
- Create an Observable Change Set from an Rx Observable with an Expiring Cache
- Create an Observable Change Set from an Observable Collection
- Create an Observable Change Set from an Binding List
- Using the ObservableChangeSet static class
- Consuming Observable Change Sets
- Observable list vs observable cache
- History of Dynamic Data
- Want to know more?
Create Dynamic Data Collections
The Observable List
Create an observable list like this:
var myInts = new SourceList<int>();
The observable list provides the direct edit methods you would expect. For example:
myInts.AddRange(Enumerable.Range(0, 10000));
myInts.Add(99999);
myInts.Remove(99999);
The AddRange, Add and Remove methods above will each produce a distinct change notification. In order to increase efficiency when making multiple amendments, the list provides a means of batch editing. This is achieved using the .Edit method which ensures only a single change notification is produced.
myInts.Edit(innerList =>
{
innerList.Clear();
innerList.AddRange(Enumerable.Range(0, 10000));
});
If myInts is to be exposed publicly it can be made read only using .AsObservableList
IObservableList<int> readonlyInts = myInts.AsObservableList();
which hides the edit methods.
The list's changes can be observed by calling myInts.Connect() like this:
IObservable<IChangeSet<int>> myIntsObservable = myInts.Connect();
This creates an observable change set for which there are dozens of operators. The changes are transmitted as an Rx observable, so they are fluent and composable.
The Observable Cache
Create an observable cache like this:
var myCache = new SourceCache<TObject,TKey>(t => key);
There are direct edit methods, for example
myCache.Clear();
myCache.AddOrUpdate(myItems);
The Clear and AddOrUpdate methods above will each produce a distinct change notification. In order to increase efficiency when making multiple amendments, the cache provides a means of batch editing. This is achieved using the .Edit method which ensures only a single change notification is produced.
myCache.Edit(innerCache =>
{
innerCache.Clear();
innerCache.AddOrUpdate(myItems);
});
If myCache is to be exposed publicly it can be made read only using .AsObservableCache
IObservableCache<TObject,TKey> readonlyCache = myCache.AsObservableCache();
which hides the edit methods.
The cache is observed by calling myCache.Connect() like this:
IObservable<IChangeSet<TObject,TKey>> myCacheObservable = myCache.Connect();
This creates an observable change set for which there are dozens of operators. The changes are transmitted as an Rx observable, so they are fluent and composable.
Creating Observable Change Sets
As stated in the introduction of this document, Dynamic Data is based on the concept of creating and manipulating observable change sets.
The primary method of creating observable change sets is to connect to instances of ISourceCache<T,K> and ISourceList<T>. There are alternative methods to produce observables change sets however, depending on the data source.
Connect to a Cache or List
Calling Connect() on a ISourceList<T> or ISourceCache<T,K> will produce an observable change set.
var myObservableChangeSet = myDynamicDataSource.Connect();
Create an Observable Change Set from an Rx Observable
Given either of the following observables:
IObservable<T> myObservable;
IObservable<IEnumerable<T>> myObservable;
an observable change set can be created like by calling .ToObservableChangeSet like this:
var myObservableChangeSet = myObservable.ToObservableChangeSet(t=> t.key);
Create an Observable Change Set from an Rx Observable with an Expiring Cache
The problem with the example above is that the internal backing cache of the observable change set will grow in size forever.
To counter this behavior, there are overloads of .ToObservableChangeSet where a size limitation or expiry time can be specified for the internal cache.
To create a time expiring cache, call .ToObservableChangeSet and specify the expiry time using the expireAfter argument:
var myConnection = myObservable.ToObservableChangeSet(t=> t.key, expireAft
