Im Februar 2019 wurde 
ReactiveUI 9 , ein plattformübergreifendes Framework zum Erstellen von GUI-Anwendungen auf der Microsoft .NET-Plattform, veröffentlicht. 
ReactiveUI ist ein Tool zur engen Integration reaktiver Erweiterungen in das MVVM-Entwurfsmuster. Die Bekanntschaft mit dem Framework kann mit einer 
Reihe von Artikeln über Habré oder 
auf der Titelseite der Dokumentation begonnen werden . Das ReactiveUI 9-Update enthält viele 
Korrekturen und Verbesserungen . Die vielleicht interessanteste und bedeutendste Änderung ist jedoch die 
enge Integration in das DynamicData-Framework , das das Arbeiten mit sich ändernden Sammlungen in einem reaktiven Stil ermöglicht. Versuchen wir herauszufinden, in welchen Fällen 
DynamicData nützlich sein kann und wie dieses leistungsstarke reaktive Framework im Inneren angeordnet ist!
Hintergrund
Zunächst definieren wir den Aufgabenbereich, den 
DynamicData löst, und finden heraus, warum Standardtools für die Arbeit mit sich ändernden Datensätzen aus dem 
System.Collections.ObjectModel Namespace nicht zu uns passen.
Wie Sie wissen, umfasst die MVVM-Vorlage die Aufteilung der Verantwortung zwischen den Ebenen des Modells, der Präsentation und des Anwendungspräsentationsmodells. Die Modellebene wird durch Domänenentitäten und -dienste dargestellt und weiß nichts über das Präsentationsmodell. Die Modellschicht kapselt die gesamte komplexe Anwendungslogik, und das Präsentationsmodell delegiert die Operationen des Modells und gibt der Ansicht über beobachtbare Eigenschaften, Befehle und Sammlungen Zugriff auf Informationen zum aktuellen Status der Anwendung. Das Standardwerkzeug zum Arbeiten mit sich ändernden Eigenschaften ist die 
INotifyPropertyChanged Schnittstelle, der 
INotifyPropertyChanged zum Arbeiten mit Benutzeraktionen und 
INotifyCollectionChanged Implementieren von Sammlungen und zum Implementieren von 
ObservableCollection und 
ReadOnlyObservableCollection .

Die Implementierung von 
INotifyPropertyChanged und 
ICommand bleibt normalerweise dem Gewissen des Entwicklers und des verwendeten MVVM-Frameworks 
ICommand , aber die Verwendung von 
ObservableCollection einer Reihe von Einschränkungen für uns! Beispielsweise können wir eine Sammlung aus einem Hintergrundthread ohne 
Dispatcher.Invoke oder einen ähnlichen Aufruf nicht ändern. Dies kann hilfreich sein, wenn Sie mit Datenarrays arbeiten, die von einer Hintergrundoperation mit dem Server synchronisiert werden. Es ist zu beachten, dass in der idiomatischen MVVM die Modellschicht nicht über die Architektur der verwendeten GUI-Anwendung Bescheid wissen und mit dem Modell von MVC oder MVP kompatibel sein muss. Aus diesem Grund ermöglichen zahlreiche 
Dispatcher.Invoke den Zugriff auf die Benutzeroberfläche über den laufenden Hintergrundthread Verstoßen Sie in einem Domänendienst gegen das Prinzip der Aufteilung der Verantwortung zwischen den Anwendungsebenen.
Natürlich wäre es in einem Domänendienst möglich, ein Ereignis zu deklarieren und als Argument eines Ereignisses einen Block mit geänderten Daten zu übergeben. Abonnieren Sie dann das Ereignis, wickeln Sie den Aufruf von 
Dispatcher.Invoke in eine Schnittstelle ein, damit er nicht vom verwendeten GUI-Framework abhängt, verschieben Sie 
Dispatcher.Invoke in das Präsentationsmodell und ändern Sie die 
ObservableCollection Bedarf. Es gibt jedoch eine viel einfachere und elegantere Möglichkeit, den angegebenen Aufgabenbereich zu lösen, ohne ein Fahrrad schreiben zu müssen . Beginnen wir mit dem Studium!
Reaktive Erweiterungen. Datenströme verwalten
Um ein umfassendes Verständnis der von 
DynamicData eingeführten 
Abstraktionen und der Prinzipien der Arbeit mit sich ändernden reaktiven Datensätzen zu erhalten, erinnern wir uns daran, 
was reaktive Programmierung ist und wie sie im Kontext der Microsoft .NET-Plattform und des MVVM-Entwurfsmusters angewendet wird . Eine Möglichkeit, die Interaktion zwischen Programmkomponenten zu organisieren, kann interaktiv und reaktiv sein. In der interaktiven Interaktion empfängt die Consumer-Funktion synchron Daten von der Provider-Funktion (Pull-basierter Ansatz, 
T , 
IEnumerable ), und in der reaktiven Interaktion liefert die Consumer-Funktion asynchron Daten an die Consumer-Funktion (Push-basierter Ansatz, 
Task , 
IObservable ).
 Reaktive Programmierung
Reaktive Programmierung ist die Programmierung mit asynchronen Datenströmen, und reaktive Erweiterungen sind ein Sonderfall ihrer Implementierung, basierend auf den 
IObservable und 
IObserver aus dem System-Namespace, der eine Reihe von LINQ-ähnlichen Operationen auf der 
IObservable Schnittstelle definiert, die als LINQ over Observable bezeichnet werden. Reaktive Erweiterungen unterstützen den .NET-Standard und funktionieren überall dort, wo die Microsoft .NET-Plattform funktioniert.

Das ReactiveUI-Framework lädt Anwendungsentwickler ein, die reaktive Implementierung von 
ICommand und 
INotifyPropertyChanged und leistungsstarke Tools wie 
ReactiveCommand<TIn, TOut> und 
WhenAnyValue . 
WhenAnyValue können 
WhenAnyValue eine Eigenschaft einer Klasse, die INotifyPropertyChanged implementiert, in einen Ereignisstrom vom Typ 
IObservable<T> , wodurch die Implementierung abhängiger Eigenschaften vereinfacht wird.
 public class ExampleViewModel : ReactiveObject { [Reactive]  
ReactiveCommand<TIn, TOut> können Sie mit dem Befehl arbeiten, wie mit einem Ereignis vom Typ 
IObservable<TOut> , das veröffentlicht wird, wenn der Befehl die Ausführung abschließt. Außerdem verfügt jeder Befehl über eine 
ThrownExceptions Eigenschaft vom Typ 
IObservable<Exception> .
 
IObservable<T> dieser ganzen Zeit haben wir mit 
IObservable<T> , wie bei einem Ereignis, das einen neuen Wert vom Typ 
T wenn sich der Status des überwachten Objekts ändert. Einfach ausgedrückt ist 
IObservable<T> ein Strom von Ereignissen, eine Sequenz, die sich über die Zeit erstreckt.
Natürlich können wir genauso einfach und natürlich mit Sammlungen arbeiten - wenn sich eine Sammlung ändert, veröffentlichen Sie eine neue Sammlung mit geänderten Elementen. In diesem Fall wäre der veröffentlichte Wert vom Typ 
IEnumerable<T> oder spezialisierter, und das Ereignis selbst wäre vom Typ 
IObservable<IEnumerable<T>> . Wie der kritisch denkende Leser jedoch richtig hervorhebt, ist dies mit ernsthaften Problemen bei der Anwendungsleistung behaftet, insbesondere wenn unsere Sammlung nicht ein Dutzend Elemente enthält, sondern hundert oder sogar mehrere Tausend!
Einführung in DynamicData
DynamicData ist eine Bibliothek, mit der Sie bei der Arbeit mit Sammlungen die volle Leistung reaktiver Erweiterungen nutzen können. Reaktive Erweiterungen bieten keine optimalen Möglichkeiten, um mit sich ändernden Datasets zu arbeiten, und 
DynamicData hat die Aufgabe, diese zu beheben. In den meisten Anwendungsanwendungen müssen Sammlungen dynamisch aktualisiert werden. In der Regel wird eine Sammlung beim Start der Anwendung mit einigen Elementen gefüllt und dann asynchron aktualisiert, um Informationen mit einem Server oder einer Datenbank zu synchronisieren. Moderne Anwendungen sind recht komplex, und häufig müssen Sammlungsprojektionen erstellt werden - Elemente filtern, transformieren oder sortieren. DynamicData wurde entwickelt, um den unglaublich komplexen Code zu beseitigen, den wir zur Verwaltung sich dynamisch ändernder Datensätze benötigen würden. Das Tool wird aktiv entwickelt und finalisiert. Mittlerweile werden mehr als 60 Bediener für die Arbeit mit Sammlungen unterstützt.
 DynamicData
DynamicData ist keine alternative Implementierung von 
ObservableCollection<T> . Die 
DynamicData- Architektur basiert hauptsächlich auf den Konzepten der domänenspezifischen Programmierung. Die Nutzungsideologie basiert auf der Tatsache, dass Sie eine Datenquelle verwalten, eine Sammlung, auf die der Code, der für die Synchronisierung und Änderung von Daten verantwortlich ist, Zugriff hat. Als Nächstes wenden Sie eine Reihe von Operatoren auf die Quelle an, mit denen Sie die Daten deklarativ transformieren können, ohne andere Sammlungen manuell erstellen und ändern zu müssen. Tatsächlich 
trennen Sie mit 
DynamicData Lese- und Schreibvorgänge und können nur reaktiv lesen. Daher werden geerbte Sammlungen immer mit der Quelle synchronisiert.
Anstelle des klassischen 
IObservable<T> definiert DynamicData Operationen für 
IObservable<IChangeSet<T>>> und 
IObservable<IChangeSet<TValue, TKey>> , wobei 
IChangeSet ein 
IChangeSet ist, der Informationen über die Sammlungsänderung enthält - die Art der Änderung und die betroffenen Elemente. Dieser Ansatz kann die Leistung von Code für die Arbeit mit Sammlungen, die in einem reaktiven Stil geschrieben wurden, erheblich verbessern. Gleichzeitig kann 
IObservable<IChangeSet<T>> immer in ein reguläres 
IObservable<IEnumerable<T>> wenn alle Elemente der Sammlung gleichzeitig verarbeitet werden müssen. Wenn es kompliziert klingt - seien Sie nicht beunruhigt, aus den Codebeispielen wird alles klar und transparent!
DynamicData-Beispiel
Schauen wir uns eine Reihe von Beispielen an, um besser zu verstehen, wie DynamicData funktioniert, wie es sich von 
System.Reactive und welche Aufgaben gewöhnliche Entwickler von Anwendungssoftware mit einer GUI lösen können. Beginnen wir mit einem umfassenden Beispiel, 
das von DynamicData auf GitHub veröffentlicht wurde . Im Beispiel ist die Datenquelle 
SourceCache<Trade, long> , der eine Sammlung von Transaktionen enthält. Die Aufgabe besteht darin, nur aktive Transaktionen anzuzeigen, Modelle in Proxy-Objekte umzuwandeln und die Sammlung zu sortieren.
 
Wenn Sie im 
SourceCache Beispiel den 
SourceCache ändern, bei dem es sich um die 
SourceCache , ändert sich auch 
SourceCache entsprechend. In diesem Fall wird beim Löschen von Elementen aus der Auflistung die 
Dispose Methode aufgerufen. Die Auflistung wird immer nur im GUI-Stream aktualisiert und bleibt sortiert und gefiltert. Cool, kein 
Dispatcher.Invoke und komplizierter Code!
SourceList- und SourceCache-Datenquellen
DynamicData bietet zwei spezialisierte Sammlungen, die als veränderbare Datenquelle verwendet werden können. Diese Sammlungen sind vom Typ 
SourceList und 
SourceCache<TObject, TKey> . Es wird empfohlen, 
SourceCache zu verwenden, wenn 
TObject einen eindeutigen Schlüssel hat, andernfalls 
SourceList . Diese Objekte bieten die bekannte .NET-Entwickler-API zum Ändern von Daten - 
Add , 
Remove , 
Insert und dergleichen. Verwenden Sie den Operator 
.Connect() um Datenquellen in 
IObservable<IChangeSet<T>> oder 
IObservable<IChangeSet<T, TKey>> .Connect() . Wenn Sie beispielsweise über einen Dienst verfügen, der die Sammlung von Elementen im Hintergrund aktualisiert, können Sie die Liste dieser Elemente problemlos mit der GUI synchronisieren, ohne 
Dispatcher.Invoke und architektonische Exzesse:
 public class BackgroundService : IBackgroundService {  
DynamicData verwendet integrierte .NET-Typen, um Daten der Außenwelt zuzuordnen. Mit den leistungsstarken DynamicData-Operatoren können wir 
IObservable<IChangeSet<Trade>> in 
ReadOnlyObservableCollection unseres Ansichtsmodells 
IObservable<IChangeSet<Trade>> .
 public class TradesViewModel : ReactiveObject { private readonly ReadOnlyObservableCollection<TradeVm> _trades; public ReadOnlyObservableCollection<TradeVm> Trades => _trades; public TradesViewModel(IBackgroundService background) {  
Neben 
Transform , 
Filter und 
Sort enthält DynamicData eine Vielzahl weiterer Operatoren, unterstützt Gruppierungen, logische Operationen, das Glätten einer Sammlung, die Verwendung von Aggregationsfunktionen, das Ausschließen identischer Elemente, das Zählen von Elementen und sogar die Virtualisierung auf der Ebene des Darstellungsmodells. Lesen Sie mehr über alle Operatoren im 
README-Projekt auf GitHub .

Sammlungen mit einem Thread und Änderungsverfolgung
Neben 
SourceList und 
SourceCache die DynamicData-Bibliothek auch eine Single-Thread-Implementierung einer veränderlichen Sammlung - 
ObservableCollectionExtended . Um zwei Sammlungen in Ihrem Ansichtsmodell zu synchronisieren, deklarieren Sie eine als 
ObservableCollectionExtended und die andere als 
ReadOnlyObservableCollection und verwenden Sie den Operator 
ToObservableChangeSet , der sich wie 
Connect verhält, jedoch für die Arbeit mit 
ObservableCollection .
 
DynamicData unterstützt auch das Verfolgen von Änderungen in Klassen, die die 
INotifyPropertyChanged Schnittstelle implementieren. Wenn Sie beispielsweise über eine Auflistungsänderung benachrichtigt werden möchten, wenn sich eine Eigenschaft eines Elements ändert, verwenden Sie die 
AutoRefresh und übergeben Sie den Selektor der gewünschten Eigenschaft mit dem Argument. 
AutoRefesh und anderen DynamicData-Operatoren können Sie die große Anzahl von Formularen und Unterformularen, die auf dem Bildschirm angezeigt werden, einfach und natürlich überprüfen!
 
Basierend auf der DynamicData-Funktionalität können Sie schnell recht komplexe Schnittstellen erstellen. Dies gilt insbesondere für Systeme, die eine große Menge von Echtzeitdaten anzeigen, Instant Messaging-Systeme und Überwachungssysteme.

Fazit
Reaktive Erweiterungen sind ein leistungsstarkes Tool, mit dem Sie deklarativ mit Daten und der Benutzeroberfläche arbeiten, tragbaren und unterstützten Code schreiben und komplexe Probleme auf einfache und elegante Weise lösen können. 
Mit ReactiveUI können .NET-Entwickler mithilfe der MVVM-Architektur reaktive Erweiterungen eng in ihre Projekte integrieren, indem sie reaktive Implementierungen von 
INotifyPropertyChanged und 
ICommand . 
DynamicData kümmert sich um die Synchronisierung der Sammlung, indem 
INotifyCollectionChanged implementiert 
INotifyCollectionChanged , wodurch die Funktionen reaktiver Erweiterungen erweitert und die Leistung 
INotifyCollectionChanged .
Die 
ReactiveUI- und 
DynamicData-Bibliotheken sind mit den gängigsten GUI-Frameworks der .NET-Plattform kompatibel, einschließlich Windows Presentation Foundation, Universal Windows Platform, 
Avalonia , Xamarin.Android, Xamarin Forms und Xamarin.iOS. Sie können DynamicData auf der 
entsprechenden ReactiveUI-Dokumentationsseite lernen. 
Schauen Sie sich auch das 
DynamicData Snippets- Projekt an, das Beispiele für die Verwendung von DynamicData für alle Gelegenheiten enthält.