head.WriteLine()

Mittwoch, März 10, 2010

TT.DOM: Change Tracking

Wie im letzten Post bereits beschrieben, stellt TT.DOM mit der Basisklasse DataObject eine Reihe von Datenbindungsfunktionen bereit.
Darüber hinaus ist DataObject aber auch in der Lage, automatisch die Originalwerte der geänderten Eigenschaften eines Objekts zu speichern. Dies ist vor allem für eine spätere Datenbankaktualisierung hilfreich. Die Änderungsverfolgung muss jedoch explizit aktiviert werden, um zu vermeiden, dass Änderungen bereits beim Befüllen der Objekte protokolliert werden. Hierfür bietet DataObject und DataObjectList<T> die Methode BeginEdit().
List<Person> persons = service.GetPersons();
var list = persons.ToDataObjectList("Id");
list.BeginEdit();

Zudem kann die Änderungsverfolgung auch durch Setzen der Mode-Eigenschaft auf Objektebene festgelegt werden.
Um festzustellen, welche Datenobjekte geändert wurden, können Sie die State-Eigenschaft abfragen. Diese enthält je nach Änderung den Wert Unchanged, Insert, Update oder Delete.
Über die Methode GetChanges() können die geänderten Objekte abgefragt werden.
List<Person> changes = list.GetChanges();
Alternativ können auch Änderungen eines bestimmten Typs abgefragt werden.
List<Person> updates = 
  list.GetChanges(DataObjectStateType.Update);

Wollen Sie die Änderungen an einen WCF-Service übertragen, können Sie die Methode SaveChanges() nutzen. Diese nimmt drei Function Delegates für die Methoden Update Insert und Delete entgegen.
list.SaveChanges<Customer>(
    c => service.UpdateCustomer(c),
    c => service.InsertCustomer(c),
    c => service.DeleteCustomer(c));

Hierbei wird davon ausgegangen, dass ein WCF-Service Proxy mit folgender Signatur existiert:
public Person UpdatePerson(Person p);
public Person InsertPerson(Person p);
public void DeletePerson(Person p);

Alternativ können Sie auch alle Änderungen an den Service übertragen und serverseitig auf den jeweiligen Änderungstyp filtern.
List<Person> changes = list.GetChanges();
service.UpdatePersons(changes);

Auf Serverseite können Sie daraufhin den Status der Objekte, sowie deren Originalwerte ermitteln.
public List<Person> UpdatePersons(List<Person> changes)
{
  foreach (Person p in changes)
  {
    switch (p.State)
    {
      case DataObjectStateType.Update:
        var orgValue = p.GetOriginalValue("Name");
        // Datensatz in DB aktualisieren
      …
    }
  }
}

Hierbei sollte die Service-Methode nicht nur eine Liste der Objekte entgegen nehmen, sondern auch zurückgeben. Dies ist erforderlich, da auf Datenbankseite z.B. Identity-Werte oder Timestamps vergeben werden, die an den Client zurück synchronisiert werden müssen.
Hierfür können Sie auf Clientseite die Methode MergeItems() aufrufen und die lokale Liste mit der serverseitigen synchronisieren.
List<Person> changes = list.GetChanges();
List<Person> refrehedList =
  service.UpdatePersons(changes);
list.MergeItems(refreshedList);

Wenn Sie jedoch oben genannte SaveChanges()-Methode aufrufen, können Sie sich diesen separaten Schritt sparen, da dieser automatisch durchgeführt wird.

Undo/Redo

Neben dem Speichern der Originalwerte, kann DataObject und DataObjectList<T> auch beliebige Änderungen rückgängig machen bzw. wiederholen. Beide Klassen bieten hierfür die Methoden Undo() und Redo(). Darüber hinaus können Sie auch einen benannten Undo-Punkt definieren, zu dem zurückgesprungen werden soll. Hierfür legen Sie zunächst über die Methode SetUndoPoint() einen Punkt an und geben diesem einen beliebigen Namen. Einer Überladung der Undo()-Methode können Sie daraufhin den Namen übergeben und alle bis dahin durchgeführten Änderungen werden entsprechen zurückgerollt.
list.SetUndoPoint("MyUndoPoint");
list[0].Name += "*";
list.Add(new Person() { Name = "Bob" };
list.Remove(1);
list.Undo("MyUndoPoint");

Wenn Sie mitbekommen wollen, ob sich die Undo- oder Redo-Liste geändert hat, können Sie sich auf das UndoListChanged bzw. RedoListChanged-Event anmelden.

Labels:

2 Comments:

  • Hi Jörg,
    ich weiß nicht, ob du es aus didaktischen Gründen so geschrieben hast:

    list.SaveChanges(
    c => service.UpdateCustomer(c),
    c => service.InsertCustomer(c),
    c => service.DeleteCustomer(c));

    Man kann es natürlich auch so schreiben:

    list.SaveChanges(
    service.UpdateCustomer,
    service.InsertCustomer,
    service.DeleteCustomer);

    Ich finde das noch etwas eleganter als mit Lambdas. Wobei es mit Lambdas deutlicher wird, dass hier Methoden reingereicht werden und keine normalen Variablen.

    TT.Dom rockt!

    Benjamin

    By Blogger HiQ-Software, at März 10, 2010 5:50 PM  

  • Hi Benjamin,

    da hast Du recht! Ich finde aber durch die explizite Syntax wird die Funktionsweise deutlicher.

    By Blogger Jörg Neumann, at März 15, 2010 8:45 AM  

Kommentar veröffentlichen

<< Home