Multi-Client Synchronisation mit Entity Framework 5 – Teil III

Nachdem ich in Teil I und Teil II das Framework EFS – Entity Framework Sync schrittweise aufgebaut habe, möchte ich nun im dritten Teil einen abschließenden Überblick über Key Facts von EFS geben.

Überblick

EFS ermöglicht die automatische Synchronisierung von geladenen Entitäten bei Datenbankänderungen durch andere Clients.

Realisiert wird dies durch Client-seitiges Loggen der Änderungen, sowie eine Polling-basierte Synchronisation.

Folgendes Klassenschema bildet dabei die Grundlage des Designs:

Logging

Entity Changes

Änderungen an Entities werden geloggt. Die Änderungen an den einzelnen Eigenschaften werden nicht separat aufgezeichnet. Eine Entity wird später als “Ganzes” neu geladen.

Collection Changes

Änderungen an Collections werden aufgezeichnet. Dies betrifft zunächst einmal die Many-to-Many Beziehungen, denn Collections in einer One-to-Many Beziehungen werden bereits implizit mit den Entity Changes aufgezeichnet, wenn man das Inverse Property Logging außen vor lässt.

Es wird lediglich gespeichert, dass eine Collection geändert wurde. Ob Elemente hinzugefügt oder gelöscht wurden, wird nicht aufgezeichnet. Dabei wird dann später die gesamte Collection neu geladen. Dies ist eine releativ einfache Implementierung ohne Seiteneffekte.

Inverse Collections

Änderungen an Many-To-Many und One-To-Many Beziehungen führen implizit zu Änderungen in den Inverse Properties. Als Grundsprinzip gilt, dass die komplizierte Logik der Beziehungsanalyse beim Loggen passieren soll. Die Synchronisierung soll später nur eine Liste von geänderten Objekt-IDs und Collections arbarbeiten.

Synchronisation

Entity Changes

Ein Objekt wird aktualisiert und aus der Datenbank geladen. Dabei werden nicht die Objekt-Beziehungen aktualisiert. Dies wird durch die API von Entity Framework ermöglicht und ist eine Grundvoraussetzung dafür, dieses EFS-Framework überhaupt realisieren zu können.

Collection Changes

Da das Loggen bereits “mundgerecht” die zu aktualisierenden Collections gespeichert hat, gilt es nur noch den Besitzer der Collection zu ermitteln und die Collection neu zu laden. Auch hier gilt, dass die API von Entity Framework dies erst einmal grundsätzlich erlaubt. Andere O/R Mapper wie z.B. LINQ-to-SQL bieten diese Möglichkeit beispielsweise nicht!

Auto Synchronization

Die Synchronisation ist getrennt vom Polling-Mechanismus implementiert. Die Auto-Synchronisation ist lediglich ein einfacher Timer, welcher in einem festen Intervall die Synchronisierungs-Logik startet.

Dispatching

Durch die Auto-Synchronization läuft die Synchronisation in einem anderen Thread, als die Haupt-Anwendung. Sowohl WPF, als auch Windows Forms besitzen eine Thread-Affinität. WPF kann eingeschränkt bei Property Änderungen in Bindings in den Haupt-Thread dispatchen, jedoch nicht bei Collection-Änderungen. Windows Forms unterstützt gar kein automatisches dispatchen.

Dem Synchronizer kann deshalb ein Dispatcher Callback übergeben werden, in dem die eigentliche Aktualisierung durchgeführt wird. Die Implementierung des Callbacks kann entweder von Windows Forms oder WPF sein, die Synchronisierungslogik bleibt dabei Technologie-neutral.

Conflict Resolution

EFS ist konfigurierbar, ob durch die Synchronisierung lokale Änderungen eines Clients überschrieben werden sollen. Es gibt hierbei die Möglichkeit StoreWins und ClientWins. Diese Einstellung kann global für den Entity Context oder für jede einzelne Entität festgelegt werden.

No-Sync Scopes

Für kritische Code-Blöcke kann die Synchronisation deaktiviert werden.

Auto-Sync Queries

Der Basisalgorithmus der Synchronisation unterstützt nur die Änderungs-Benachrichtigung innerhalb von Objektgraphen, d.h. solange ein Root-Objekt vorhanden ist.

Ergebnisse von Queries besitzen kein Oberobjekt. Zwar werden die beinhalteten Objekte bei Änderungen aktualisiert, jedoch nicht die Liste selbst.

Um dies zu unterstützen, können Query-Wrapper für Auto-Sync Queries verwendet werden. Zunächst genügt eine einfache Implementierung, welche den Query erneut ausführt, sobald irgend ein Objekt vom Typ der verwendeten Entität verändert wurde. Zur Optimierung wird das Neu-Laden erst nach Abschluss des vollständigen Synchronisierungsvorgangs durchgeführt. Hier wäre auch eine ausgeklügeltere Methodik denkbar.

Registration & Log Disposal

Die Logs sind nur temporäre Steuerungsdaten und werden gelöscht, nachdem sie nicht mehr benötigt werden. Um festzustellen, wann sie nicht mehr benötigt werden, werden alle Synchronizer-Instanzen in der Datenbank mit einer GUID und der zuletzt verarbeiteten Revision registriert. Nach jeder Synchronisation wird der “letzte Synchronizer” und dessen Revision ermittelt. Logs können gelöscht werden, sobald sie von allen Synchronizern verarbeitet wurden.

Synchronizer-Objekte selbst können ebenso zu Orphans in der Datenbank werden, falls der Client z.B. durch einen Absturz den Eintrag nicht aus der Datenbank entfernt hat. Hier gibt es eine Keep-Alive Regelung, die inaktive Synchronizer autmomatisch löscht. Hier ist ein großzügiges Timeout z.B. von einem Tag genügend.

Integration

Entity Framework kann nicht ohne weiteres in jede Anwendung integriert werden. Falls eine WPF-Anwendung in der Oberfläche beispielsweise direkt Entity Collections binden soll, ist dies mit Entity Framework out-of-the box nicht möglich, da die Collections vom Typ EntitySet sind und nicht INotifyCollectionChanged implementieren.

Hierzu gibt es ein übliches Pattern von Decorator-Collections. Diese würden jedoch von der Synchronsisierung selbst nichts mitbekommen, deshalb können dieses mittels Attributen gekennzeichnet werden.

Somit können diese in der Synchronisation ebenso erkannt und synchronisiert werden.

Ziel-Plattformen

Entity Framework unterstützt mit Code First und Model First unterschiedliche Ansätze, die teilweise abweichende APIs besitzen. Beide sollen unterstützt werden. .NET 4.0 sollte noch unterstützt werden. Entwickelt wird das Framework für Version 5 von Entity Framework. Die neue Version 6 sollte ebenso unterstützt werden.

Zunächst ist die Synchronisation für SQL Server ausgelegt, jedoch ist der Datenzugriff gekapselt, sodass auch andere DBMS später unterstützt werden können.

Git Repository

Der Quellcode wird auf Sourceforge gehostet.

Serie

Projekt bei Sourceforge