DynamicData: Changing collections, MVVM design pattern, and reactive extensions
Background
First, we define the range of tasks that DynamicData solves and find out why standard tools for working with changing data sets from the namespace do not suit us
System.Collections.ObjectModel. The MVVM template, as you know, involves the division of responsibility between the layers of the model, presentation and presentation model of the application. The model layer is represented by domain entities and services, and knows nothing about the presentation model. The model layer encapsulates the entire complex application logic, and the presentation model delegates the modelβs operations, giving the view access to information about the current state of the application through observable properties, commands, and collections. The standard tool for working with changing properties is the interface
INotifyPropertyChanged, for working with user actions -ICommand, and to work with collections - INotifyCollectionChangedand implementations ObservableCollectionand ReadOnlyObservableCollection. 
Implementation
INotifyPropertyChangedand ICommandusually remains on the conscience of the developer and used MVVM framework, but the use ObservableCollectionimposes a number of restrictions on us! For example, we cannot change a collection from a background thread without Dispatcher.Invokeor a similar call, and this could be useful in case of working with data arrays that some background operation synchronizes with the server. It should be noted that in the idiomatic MVVM the model layer does not need to know about the architecture of the GUI application used, and be compatible with the model from MVC or MVP, and that is why numerousDispatcher.Invoke, allowing access to the user interface control from the background thread running in the domain service, violate the principle of sharing responsibility between application layers. Of course, in a domain service it would be possible to declare an event, and as an argument of an event, pass a chunk with changed data. Then subscribe to the event, wrap the call
Dispatcher.Invokein an interface so that it does not depend on the GUI framework used, move it Dispatcher.Invoketo the presentation model and change ObservableCollectionit as needed, however there is a much simpler and more elegant way to solve the indicated range of tasks without writing a bicycle. Let's start the study!Reactive extensions. Manage data streams
For a complete understanding of the abstractions introduced by DynamicData and the principles of working with changing reactive datasets, let's recall what reactive programming is and how to apply it in the context of the Microsoft .NET platform and the MVVM design pattern . A way to organize the interaction between program components can be interactive and reactive. When the interaction function is interactive, the user receives synchronous data from the function-provider (pull-based approach
T, IEnumerable), and interaction with reactive function provider provides data asynchronously consuming functions (push-based approach Task, IObservable). 
Reactive programmingThis is programming using asynchronous data streams, and reactive extensions are a special case of its implementation, based on interfaces
IObservableand IObserverfrom the System namespace, which defines a number of LINQ-like operations on an interface IObservablecalled LINQ over Observable. Reactive extensions support the .NET Standard and work wherever the Microsoft .NET platform works. 
The ReactiveUI framework offers application developers to take advantage of the reactive implementation of interfaces
ICommandand INotifyPropertyChangedby providing such powerful tools as and . allows you to convert a property of a class that implements INotifyPropertyChanged into an event stream of a type , which simplifies the implementation of dependent properties.ReactiveCommandWhenAnyValueWhenAnyValueIObservablepublic class ExampleViewModel : ReactiveObject
{
[Reactive]
// ΠΡΡΠΈΠ±ΡΡ ReactiveUI.Fody, Π·Π°Π½ΠΈΠΌΠ°Π΅ΡΡΡ
// Π°ΡΠΏΠ΅ΠΊΡΠ½ΠΎ-ΠΎΡΠΈΠ΅Π½ΡΠΈΡΠΎΠ²Π°Π½Π½ΡΠΌ Π²Π½Π΅Π΄ΡΠ΅Π½ΠΈΠ΅ΠΌ
// OnPropertyChanged Π² ΡΠ΅ΡΡΠ΅Ρ Name.
public string Name { get; set; }
public ExampleViewModel()
{
// Π‘Π»ΡΡΠ°Π΅ΠΌ OnPropertyChanged("Name").
this.WhenAnyValue(x => x.Name)
// Π Π°Π±ΠΎΡΠ°Π΅ΠΌ Ρ IObservable
.Subscribe(Console.WriteLine);
}
}
ReactiveCommandallows you to work with the team as an event of the type that is published whenever the team completes execution. Also, any command has a type property .IObservableThrownExceptionsIObservable// ReactiveCommand
var command = ReactiveCommand.Create(() => 42);
command
// Π Π°Π±ΠΎΡΠ°Π΅ΠΌ Ρ IObservable
.Subscribe(Console.WriteLine);
command
.ThrownExceptions
// Π Π°Π±ΠΎΡΠ°Π΅ΠΌ Ρ IObservable
.Select(exception => exception.Message)
// Π Π°Π±ΠΎΡΠ°Π΅ΠΌ Ρ IObservable
.Subscribe(Console.WriteLine);
command.Execute().Subscribe();
// ΠΡΠ²ΠΎΠ΄: 42
All this time we worked with , as with an event that publishes a new type value whenever the state of the object being monitored changes. Simply put, it is a stream of events, a sequence stretched over time. Of course, we could just as easily and naturally work with collections - whenever a collection changes, publish a new collection with changed elements. In this case, the published value would have a type or more specialized, and the event itself would have a type . But, as the critically thinking reader correctly points out, this is fraught with serious problems with application performance, especially if there are not a dozen elements in our collection, but a hundred, or even several thousand!
IObservableTIObservableIEnumerableIObservable> Introduction to DynamicData
DynamicData β is a library that allows you to use the full power of reactive extensions when working with collections. Out of the box reactive extensions do not provide optimal ways to work with changing datasets, and the DynamicData task- to fix this. In most applications, there is a need to dynamically update collections - usually, a collection is filled with some elements when the application starts, and then it is updated asynchronously, synchronizing information with a server or database. Modern applications are quite complex, and often there is a need to create collections projections - filter, transform or sort elements. DynamicData was designed just to get rid of the incredibly complex code that we would need to manage dynamically changing data sets. The tool is actively developed and finalized, and now more than 60 operators for working with collections are supported. DynamicData is not an alternative implementation . Architecture

ObservableCollectionDynamicData is based primarily on the concepts of subject-oriented programming. The ideology of use is based on the fact that you manage some data source, a collection that the code responsible for synchronizing and changing data has access to. Next, you apply a number of operators to the source, with which you can declaratively transform the data, without the need to manually create and modify other collections. In fact, with DynamicData you separate read and write operations, and you can only read in a reactive way - therefore, inherited collections will always be synchronized with the source. Instead of the classic , DynamicData defines operations on and , where
IObservableIObservable>> IObservable> IChangeSet- a chunk containing information about the change in the collection β the type of change and the elements that have been affected. This approach can significantly improve the performance of code for working with collections written in a reactive style. At the same time, you can always transform into ordinary , if it becomes necessary to process all the elements of the collection at once. If it sounds complicated - do not be alarmed, from the code examples everything will become clear and transparent!IObservable> IObservable> DynamicData example
Let's look at a series of examples to better understand how DynamicData works, how it differs from
System.Reactiveand what tasks ordinary developers of application software with a GUI can solve. Let's start with a comprehensive example posted by DynamicData on GitHub . In the example, the data source is a containing a collection of transactions. The task is to show only active transactions, transform models into proxy objects, sort the collection.SourceCache// Π‘ΡΠ°Π½Π΄Π°ΡΡΠ½Π°Ρ ΠΊΠΎΠ»Π»Π΅ΠΊΡΠΈΡ ΠΈΠ· System.Collections.ObjectModel,
// ΠΊ ΠΊΠΎΡΠΎΡΠΎΠΉ Π±ΡΠ΄Π΅Ρ ΠΏΡΠΈΠ²ΡΠ·Π°Π½ Π³ΡΠ°ΡΠΈΡΠ΅ΡΠΊΠΈΠΉ ΡΠ»Π΅ΠΌΠ΅Π½Ρ ΡΠΏΡΠ°Π²Π»Π΅Π½ΠΈΡ.
ReadOnlyObservableCollection list;
// ΠΠ·ΠΌΠ΅Π½ΡΠ΅ΠΌΡΠΉ ΠΈΡΡΠΎΡΠ½ΠΈΠΊ Π΄Π°Π½Π½ΡΡ
, ΡΠΎΠ΄Π΅ΡΠΆΠ°ΡΠΈΠΉ ΠΌΠΎΠ΄Π΅Π»ΠΈ ΡΠ΄Π΅Π»ΠΎΠΊ.
// ΠΠΎΠΆΠ΅ΠΌ ΠΈΡΠΏΠΎΠ»ΡΠ·ΠΎΠ²Π°ΡΡ Add, Remove, Insert ΠΈ ΠΏΠΎΠ΄ΠΎΠ±Π½ΡΠ΅ ΠΌΠ΅ΡΠΎΠ΄Ρ.
var source = new SourceCache(trade => trade.Id);
var cancellation = source
// Π’ΡΠ°Π½ΡΡΠΎΡΠΌΠΈΡΡΠ΅ΠΌ ΠΈΡΡΠΎΡΠ½ΠΈΠΊ Π² Π½Π°Π±Π»ΡΠ΄Π°Π΅ΠΌΡΠΉ Π½Π°Π±ΠΎΡ ΠΈΠ·ΠΌΠ΅Π½Π΅Π½ΠΈΠΉ.
// ΠΠΌΠ΅Π΅ΠΌ ΡΠΈΠΏ IObservable>
.Connect()
// ΠΠ°Π»ΡΡΠ΅ ΠΏΠΎ ΡΠ΅ΠΏΠΎΡΠΊΠ΅ ΠΏΡΠΎΠΏΡΡΡΠΈΠΌ ΡΠΎΠ»ΡΠΊΠΎ Π°ΠΊΡΠΈΠ²Π½ΡΠ΅ ΡΠ΄Π΅Π»ΠΊΠΈ.
.Filter(trade => trade.Status == TradeStatus.Live)
// Π’ΡΠ°Π½ΡΡΠΎΡΠΌΠΈΡΡΠ΅ΠΌ ΠΌΠΎΠ΄Π΅Π»ΠΈ Π² ΠΏΡΠΎΠΊΡΠΈ-ΠΎΠ±ΡΠ΅ΠΊΡΡ.
// ΠΠΌΠ΅Π΅ΠΌ ΡΠΈΠΏ IObservable>
.Transform(trade => new TradeProxy(trade))
// ΠΡΡΠΎΡΡΠΈΡΡΠ΅ΠΌ ΠΎΠ±ΡΠ΅ΠΊΡΡ ΠΏΠΎ Π²ΡΠ΅ΠΌΠ΅Π½ΠΈ.
.Sort(SortExpressionComparer
.Descending(trade => trade.Timestamp))
// ΠΠ±Π½ΠΎΠ²Π»ΡΠ΅ΠΌ GUI ΡΠΎΠ»ΡΠΊΠΎ ΠΈΠ· Π³Π»Π°Π²Π½ΠΎΠ³ΠΎ ΠΏΠΎΡΠΎΠΊΠ°.
.ObserveOnDispatcher()
// ΠΡΠΈΠ²ΡΠΆΠ΅ΠΌ ΡΠΏΠΈΡΠΎΠΊ ΠΎΡΡΠΎΡΡΠΈΡΠΎΠ²Π°Π½Π½ΡΡ
ΠΏΡΠΎΠΊΡΠΈ-ΠΎΠ±ΡΠ΅ΠΊΡΠΎΠ²
// ΠΊ ΠΊΠΎΠ»Π»Π΅ΠΊΡΠΈΠΈ ΠΈΠ· System.Collections.ObjectModel.
.Bind(out list)
// Π£Π±Π΅Π΄ΠΈΠΌΡΡ, ΡΡΠΎ ΠΏΠΎ ΠΌΠ΅ΡΠ΅ ΡΠ΄Π°Π»Π΅Π½ΠΈΡ ΡΠ»Π΅ΠΌΠ΅Π½ΡΠΎΠ² ΠΈΠ·
// ΠΊΠΎΠ»Π»Π΅ΠΊΡΠΈΠΈ ΡΠ΅ΡΡΡΡΡ Π±ΡΠ΄ΡΡ ΠΎΡΠ²ΠΎΠ±ΠΎΠΆΠ΄Π΅Π½Ρ.
.DisposeMany()
.Subscribe();
In the example above, a change
SourceCachethat is a data source will ReadOnlyObservableCollectionalso change accordingly. At the same time, when deleting elements from the collection, the method will be called Dispose, the collection will always be updated only in the GUI stream and remain sorted and filtered. Cool, no Dispatcher.Invokecomplicated code!SourceList and SourceCache Data Sources
DynamicData provides two specialized collections that can be used as a mutable data source. These collections are types
SourceListand . It is recommended to use it whenever it has a unique key, otherwise use it . These objects provide familiar .NET API for developers to change the data - the methods , , and the like. To convert data sources to or , use the operator . For example, if you have a service that updates the collection of elements in the background, you can easily synchronize the list of these elements with the GUI, without any architectural excesses:SourceCacheSourceCacheTObjectSourceListAddRemoveInsertIObservable> IObservable> .Connect()Dispatcher.Invokepublic class BackgroundService : IBackgroundService
{
// ΠΠ±ΡΡΠ²Π»ΡΠ΅ΠΌ ΠΈΠ·ΠΌΠ΅Π½ΡΠ΅ΠΌΡΠΉ ΡΠΏΠΈΡΠΎΠΊ ΡΠ΄Π΅Π»ΠΎΠΊ.
private readonly SourceList _trades;
// ΠΡΡΡΠ°Π²Π»ΡΠ΅ΠΌ Π½Π°ΡΡΠΆΡ ΠΏΠΎΡΠΎΠΊ ΠΈΠ·ΠΌΠ΅Π½Π΅Π½ΠΈΠΉ ΠΊΠΎΠ»Π»Π΅ΠΊΡΠΈΠΈ.
// ΠΡΠ»ΠΈ ΠΎΠΆΠΈΠ΄Π°Π΅ΡΡΡ, ΡΡΠΎ Π±ΡΠ΄Π΅Ρ Π±ΠΎΠ»Π΅Π΅ ΠΎΠ΄Π½ΠΎΠ³ΠΎ ΠΏΠΎΠ΄ΠΏΠΈΡΡΠΈΠΊΠ°,
// ΡΠ΅ΠΊΠΎΠΌΠ΅Π½Π΄ΡΠ΅ΡΡΡ ΠΈΡΠΏΠΎΠ»ΡΠ·ΠΎΠ²Π°ΡΡ ΠΎΠΏΠ΅ΡΠ°ΡΠΎΡ Publish() ΠΈΠ· Rx.
public IObservable> Connect() => _trades.Connect();
public BackgroundService()
{
_trades = new SourceList();
_trades.Add(new Trade());
// ΠΠ·ΠΌΠ΅Π½ΡΠ΅ΠΌ ΡΠΏΠΈΡΠΎΠΊ ΠΊΠ°ΠΊ Ρ
ΠΎΡΠΈΠΌ!
// ΠΠ°ΠΆΠ΅ ΠΈΠ· ΡΠΎΠ½ΠΎΠ²ΠΎΠ³ΠΎ ΠΏΠΎΡΠΎΠΊΠ°.
}
}
DynamicData uses built-in .NET types to map data to the outside world. Using powerful DynamicData operators, we can transform views into our model.
IObservable> ReadOnlyObservableCollectionpublic class TradesViewModel : ReactiveObject
{
private readonly ReadOnlyObservableCollection _trades;
public ReadOnlyObservableCollection Trades => _trades;
public TradesViewModel(IBackgroundService background)
{
// ΠΠΎΠ΄ΠΊΠ»ΡΡΠΈΠΌΡΡ ΠΊ ΠΈΡΡΠΎΡΠ½ΠΈΠΊΡ, ΡΡΠ°Π½ΡΡΠΎΡΠΌΠΈΡΡΠ΅ΠΌ ΡΠ»Π΅ΠΌΠ΅Π½ΡΡ, ΠΏΡΠΈΠ²ΡΠΆΠ΅ΠΌ
// ΠΈΡ
ΠΊ ΠΊΠΎΠ»Π»Π΅ΠΊΡΠΈΠΈ ΠΈΠ· System.Collections.ObjectModel.
background.Connect()
.Transform(x => new TradeVm(x))
.ObserveOn(RxApp.MainThreadScheduler)
.Bind(out _trades)
.DisposeMany()
.Subscribe();
}
}
Additionally
Transform, Filterand Sort, DynamicData includes numerous other operators supports grouping, logical operations, smoothing collection application aggregated functions exception identical elements, counting elements and even at the level virtualization model representation. Read more about all operators in the README project on GitHub .
Single threaded collections and change tracking
Besides
SourceListand SourceCache, the DynamicData library also includes a single-threaded implementation of a mutable collection - ObservableCollectionExtended. To synchronize two collections in your view model, declare one of them as ObservableCollectionExtended, and the other as ReadOnlyObservableCollectionand use an operator ToObservableChangeSetthat behaves the same as it is Connect, but is designed to work with ObservableCollection.// ΠΠ±ΡΡΠ²ΠΈΠΌ ΠΏΡΠΎΠΈΠ·Π²ΠΎΠ΄Π½ΡΡ ΠΊΠΎΠ»Π»Π΅ΠΊΡΠΈΡ.
ReadOnlyObservableCollection _derived;
// ΠΠ±ΡΡΠ²ΠΈΠΌ ΠΈ ΠΈΠ½ΠΈΡΠΈΠ°Π»ΠΈΠ·ΠΈΡΠ΅ΠΌ ΠΊΠΎΠ»Π»Π΅ΠΊΡΠΈΡ-ΠΈΡΡΠΎΡΠ½ΠΈΠΊ.
var source = new ObservableCollectionExtended();
source.ToObservableChangeSet(trade => trade.Key)
.Transform(trade => new TradeProxy(trade))
.Filter(proxy => proxy.IsChecked)
.Bind(out _derived)
.Subscribe();
DynamicData also supports tracking changes to classes that implement the interface
INotifyPropertyChanged. For example, if you want to be notified of a collection change whenever a property of an element changes, use the operator AutoRefreshand pass the selector of the desired property with the argument. AutoRefeshand other DynamicData operators will allow you to easily and naturally validate the huge number of forms and nested forms displayed on the screen!// Π’ΠΈΠΏ IObservable
var isValid = databases
.ToObservableChangeSet()
// ΠΠΎΠ΄ΠΏΠΈΡΡΠ²Π°Π΅ΠΌΡΡ ΡΠΎΠ»ΡΠΊΠΎ Π½Π° ΠΈΠ·ΠΌΠ΅Π½Π΅Π½ΠΈΡ ΡΠ²ΠΎΠΉΡΡΠ²Π° IsValid.
.AutoRefresh(database => database.IsValid)
// ΠΠΎΠ»ΡΡΠ°Π΅ΠΌ Π΄ΠΎΡΡΡΠΏ ΠΊΠΎ Π²ΡΠ΅ΠΌ ΡΠ»Π΅ΠΌΠ΅Π½ΡΠ°ΠΌ ΠΊΠΎΠ»Π»Π΅ΠΊΡΠΈΠΈ ΠΎΠ΄Π½ΠΎΠ²ΡΠ΅ΠΌΠ΅Π½Π½ΠΎ.
.ToCollection()
// ΠΡΠΎΠ²Π΅ΡΡΠ΅ΠΌ, ΡΡΠΎ Π²ΡΠ΅ ΡΠ»Π΅ΠΌΠ΅Π½ΡΡ Π²Π°Π»ΠΈΠ΄Π½Ρ.
.Select(x => x.All(y => y.IsValid));
// ΠΡΠ»ΠΈ ΠΈΡΠΏΠΎΠ»ΡΠ·ΡΠ΅ΡΡΡ ReactiveUI, IObservable
// ΠΌΠΎΠΆΠ½ΠΎ ΠΏΡΠ΅ΠΎΠ±ΡΠ°Π·ΠΎΠ²Π°ΡΡ Π² ΡΠ²ΠΎΠΉΡΡΠ²ΠΎ ObservableAsProperty.
_isValid = isValid
.ObserveOn(RxApp.MainThreadScheduler)
.ToProperty(this, x => x.IsValid);
Based on the DynamicData functionality, you can quickly create fairly complex interfaces - this is especially true for systems that display a large amount of real-time data, instant messaging systems, and monitoring systems.

Conclusion
Reactive extensions are a powerful tool that allows you to declaratively work with data and the user interface, write portable and supported code, and solve complex problems in a simple and elegant way. ReactiveUI allows .NET developers to closely integrate reactive extensions into their projects using the MVVM architecture by providing reactive implementations
INotifyPropertyChangedand ICommand, and DynamicData takes care of collection synchronization by implementing INotifyCollectionChanged, expanding the capabilities of reactive extensions and taking care of performance. Libraries ReactiveUI and DynamicDatacompatible with most popular .NET framework GUIs, including Windows Presentation Foundation, Universal Windows Platform, Avalonia , Xamarin.Android, Xamarin Forms, Xamarin.iOS. You can start learning DynamicData from the corresponding ReactiveUI documentation page . Also be sure to check out the DynamicData Snippets project , which contains examples of using DynamicData for all occasions.