LightSwitch – Genauer hingeschaut Teil 2 (Teil 4)
Kommentare

Änderungen handhaben mithilfe von Changesets
Bevor LightSwitch irgendwelche Änderungen in die Datenbank übernimmt, behält es sich auf Speicherebene auf Entitätsebene eine Liste der geänderten Informationen

Änderungen handhaben mithilfe von Changesets

Bevor LightSwitch irgendwelche Änderungen in die Datenbank übernimmt, behält es sich auf Speicherebene auf Entitätsebene eine Liste der geänderten Informationen und organisiert diese in so genannten Changesets. Damit hat man jederzeit die Möglichkeit, vom Benutzer eingegebene Änderungen zu verwerfen (oder aber auch direkt persistent zu machen). Am sinnvollsten ist dies natürlich, wenn der Benutzer selbst die eingegebenen Daten verwirft. Zu diesem Zweck empfiehlt es sich, beispielsweise eine eigene Schaltfläche UNDO für den Endbenutzer zu entwerfen und diese das Handling für das Verwerfen der modifizierten Daten übernehmen zu lassen. Das ist mit den Changesets nicht weiter schwierig – und noch dazu hat man den Vorteil, dass man sich als Entwickler nicht um die Details der eingegebenen Daten kümmern muss, sondern einfach mit dem gesamten Changeset arbeiten kann. Der erste Schritt zum Implementieren des Undo-Verhaltens ist es, die entsprechende Schaltfläche im Screen-Designer hinzuzufügen, z. B. so wie in Abbildung 8.

Abb. 8: Hinzugefügte Schaltfläche Undo und das Kontextmenü zum Editieren des Codes
Abb. 8: Hinzugefügte Schaltfläche Undo und das Kontextmenü zum Editieren des Codes

Ein Rechtsklick auf das Kontextmenü ermöglicht es, den Execute-Code zu editieren. Die generierte Methode Button2_Execute gehört in die Kategorie der Screen Events (Tabelle 1) und ermöglicht ein direktes Eingreifen auf den Klick einer Schaltfläche. Die Tatsache, dass die Methode bei der automatischen Erstellung nicht wie erwartet Undo_Execute genannt wurde, liegt daran, dass Schaltflächen beim Hinzufügen einen automatischen Namen bekommen; auch nach dem Umbenennen der Komponente behält das System den ursprünglichen Namen für die Nomenklatur der Ereignismethoden. Hier wäre es wünschenswert, wenn der tatsächliche Name übernommen werden würde – aber LightSwitch befindet sich noch in der Betaversion 1.

Die eigentliche Implementierung des Changeset Handlings erfordert nur wenige Handgriffe. Wie gewohnt, sind nicht nur die LightSwitch-Designer einfach zu bedienen, sondern auch das API. Jede Datenquelle besitzt eine Eigenschaft Details(Typ: DetailsClass), die wiederum mehrere Eigenschaften enthält, anhand derer man die Datenquelle auf Änderungen überprüfen, die Änderungen verwerfen oder aber einfach das ChangeSet auslesen und mit diesem arbeiten kann. Listing 2 zeigt, wie die Datenquelle zuerst auf Änderungen überprüft wird (mithilfe der Eigenschaft HasChanges). Danach wird das eigentliche Changeset zugewiesen. 

Listing 2: Programmatisches Verwerfen von Benutzereingaben

partial void Button2_Execute()
{
    if (this.DataWorkspace.ApplicationData.Details.HasChanges)
    {
        EntityChangeSet changeSet = this.DataWorkspace.ApplicationData.Details.GetChanges();
           foreach (IEntityObject item in changeSet.ModifiedEntities)
            {
                if (item.GetType() == typeof(Department))
                {
                    item.Details.DiscardChanges();
                }
            }
     }
}  

Das passiert durch den Aufruf der GetChanges-Methode und durch die Zuweisung des resultierenden Changesets an die Variable changeSet des Typs EntityChangeSet. changeSet enthält Eigenschaften für veränderte (ModifiedEntities), hinzugefügte (AddedEntities) und gelöschte (DeletedEntities) Entitäten. Diese sind Auflistungen von Objekten, die die Schnittstelle IEntityObject implementieren. Man findet die Objekte im Changeset also einerseits schon gruppiert nach der Art der Datenmanipulation vor und erhält zusätzlich noch die Möglichkeit, diese Objekte auf ihren Typ hin zu überprüfen, um so gezielt nur Entitäten eines bestimmten Typs zu verwerfen. Denn jedes Objekt im ChangeSet enthält eine Details-Eigenschaft, die das Verwerfen von Änderungen auf Objektebene ermöglicht (und zwar mithilfe der Methode DiscardChanges). Listing 2 zeigt die vorherige Überprüfung des IEntityObjects auf seinen Typ (item.GetType() unter Zuhilfenahme von Reflection). Im darauf folgenden Schritt werden alle Entitäten des Typs Department verworfen, sofern sie verändert wurden. Alle hinzugefügten oder gelöschten Entitäten würden bei einer Save-Anweisung persistent gemacht und in die Datenbank geschrieben, da sie nicht behandelt wurden. Wir haben nun also eine Undo-Funktionalität entwickelt, die (zumindest Out of the box) in LightSwitch nicht enthalten ist. Allerdings wurde dafür jedes einzelne Objekt auf seinen Typ hin überprüft und dessen Änderungen daraufhin verworfen. In manchen Fällen ist dies aber zu viel des Aufwands, und man möchte vielleicht bei einer Standard-Undo-Funktionalität einfach alle eingegebenen Änderungen verwerfen – ganz gleich, welchem Typ die involvierten Objekte angehören. Dafür enthält das API die Möglichkeit, wie bereits vorhin angekündigt, auf Ebene des gesamten Changesets Änderungen rückgängig zu machen. In diesem Fall würde man die Methode Button2_Execute einfach wie in Listing 3 implementieren und auf Ebene der Datenquelle die Änderungen rückgängig machen, indem man DiscardChanges() auf dem Details-Objekt der Datenquelle ausführt (im vorliegenden Fall ist dies die Standarddatenquelle ApplicationData).

Konkurrenz

Wie bei jeder datenbankbasierten Anwendung sollte auch in LightSwitch Nebenläufigkeit behandelt werden. Stellen Sie sich vor, es existierten verschiedene Schnittstellen zur bereits erstellten Human-Resource-Datenbank, etwa eine Webapplikation, die die Verwaltung der Stammdaten ermöglicht, oder aber auch eine Client-Applikation. Es könnte sein, dass während man fleißig Daten mithilfe der LightSwitch-Applikation einklopft, und während die veränderten Daten bereits reihenweise im Speicher existieren, verändert jemand an anderer Stelle die Daten, die ursprünglich geholt wurden. Klarerweise sind die Daten damit nicht mehr synchronisiert, und es entsteht das Problem der Concurrency. Im genannten Fall würde beim Aufruf der SaveChanges()-Methode eine Ausnahme auftreten, die man tunlichst behandeln sollte. LightSwitch bietet hierfür eine spezielle Klasse an, EntityConflict, mithilfe derer man bestimmen kann, welches Problem aufgetreten ist und dieses gezielt behandeln kann. Für dieses Beispiel empfiehlt es sich, sinnigerweise ein Daten-Event zu behandeln. Im vorliegenden Beispiel aus Listing 3 handelt es sich um das Inserting-Event der Entität Person. Entsprechend wurde per Designer über Write Code das Grundgerüst für die Ereignismethode People_Inserting erstellt. Im Prinzip macht die Methode nicht mehr, als SaveChanges() auszuführen – diesmal aber mit dem Unterschied, dass die Anweisung in einen try-catch-Block kommt. Sollte ein Problem mit der Concurrency auftreten, so tritt zur Laufzeit eine ConcurrencyException auf – und diese will behandelt werden. Dieser Ausnahmetyp enthält praktischerweise eine Auflistung, die alle betroffenen Objekte enthält (EntitiesWithConflics) und macht diese über eine Objekteigenschaft zugänglich. Innerhalb der Ausnahmebehandlung kann man als Entwickler entscheiden, wie mit den Konflikten umgegangen werden soll.

Listing 3: Ausnahmebehandlung bei einer ConcurrencyException

partial void People_Inserting(Person entity)
{
    try
    {
        this.DataWorkspace.ApplicationData.SaveChanges();
    }
    catch (ConcurrencyException e)
    {
        foreach (IEntityObject conflictObject in e.EntitiesWithConflicts)
        {
            EntityConflict conflict = conflictObject.Details.EntityConflict;
            if (conflict.IsDeletedOnServer)
            {
                conflict.ResolveConflicts(ConflictResolution.ServerWins);
            }
            else
            {
                conflict.ResolveConflicts(ConflictResolution.ClientWins);
            }
        }
    }
}  
Unsere Redaktion empfiehlt:

Relevante Beiträge

Meinungen zu diesem Beitrag

X
- Gib Deinen Standort ein -
- or -