Wie fügt man Objektgraphen nach NHibernate StaleObjectStateException elegant zusammen?

9

Wir versuchen, Objekte zu kombinieren, nachdem eine StaleObjectStateException ausgelöst wurde, um eine zusammengeführte Kopie zu speichern.

Hier ist unsere Umweltsituation:

  • Listenelement
  • Mehrbenutzersystem
  • WPF Desktop-Anwendung, SQL Server 2008-Datenbank
  • NHibernate 3.1.0.4000, FluentNHibernate 1.2.0.712
  • Globale, lang laufende NHibernate-Sitzungen [im Moment. Wir verstehen, dass Sitzung pro Moderator das empfohlene Muster ist, aber in unserem Projektplan derzeit keine Zeit für die Konvertierung hat.]
  • Top-down-Speicherung und Eigenschaftsnavigation (das heißt, wir speichern das Top-Level-Objekt (hier Eltern genannt) in unserem Domain-Graphen)
  • .Cascade.AllDeleteOrphan () wird in den meisten Fällen verwendet.
  • Benutzer besitzen ausschließlich einige Objekte im Domänen-Graphen, besitzen jedoch den Eigentümer des übergeordneten Objekts.
  • Navigationseigenschaften auf Children-Objekte existieren nicht.
  • Alle Klassen haben numerische ID- und numerische Versionsfelder.

Anwendungsfall:

  • Benutzer 1 startet die Anwendung und öffnet Parent.
  • Benutzer 2 startet die Anwendung und öffnet Parent.
  • Benutzer 2 fügt ein Kind hinzu (hier C2).
  • Benutzer 2 speichert Parent.
  • Benutzer 1 fügt ein Kind hinzu (hier C1).
  • Benutzer 1 speichert Parent.
  • Benutzer 1 erhält eine StaleObjectStateException (und das zu Recht)

Wir möchten elegant die Ausnahme behandeln. Da die Benutzer den Besitz des übergeordneten Elements teilen, sollte Benutzer 1 in der Lage sein, erfolgreich zu speichern und das übergeordnete Element sowohl mit seinem neuen Kind als auch mit dem untergeordneten Element von Benutzer 2 zu speichern.

Wenn SOSE laut Ayende ( Ссылка ) geworfen wird:

  

Ihre Sitzung und ihre geladenen Entitäten sind Toast, weil mit NHibernate eine Ausnahme ausgelöst wird   aus einer Sitzung verschiebt diese Sitzung in einen undefinierten Zustand. Sie können diese Sitzung nicht mehr verwenden   oder geladene Entitäten

C1 wurde bereits eine ID und Version # von der jetzt nicht nützlichen Sitzung zugewiesen. (Ich wünschte, es wäre nicht gewesen.)

Wie kombinieren wir die Verwendung von ISession.Merge () und ISession.Refresh (), um ein neu gespeichertes Parent zu erhalten, das sowohl C1 als auch C2 hat?

Wir haben eine Reihe von arkanen Permutationen ausprobiert, von denen keine vollständig funktioniert. In der Regel wurde entweder eine Zeile durch eine andere Transaktion aktualisiert oder gelöscht (oder die Zuordnung von nicht gespeicherten Werten war falsch) oder eine ID-Kollision auf der ODBC-Ebene.

Unsere Theorie im Moment:

  1. Setzen Sie die Versionsnummern auf C1 zurück (um zu verhindern, dass "nicht gespeichertes Wert-Mapping falsch war")
  2. Erhalte eine neue Sitzung
  3. newSession.Refresh (C1);
  4. newParent = newSession.QueryOver [...]
  5. newParent.Add (C1);
  6. newSession.SaveOrUpdate (neuesParent)

Die gesamte Dokumentation legt jedoch nahe, dass newSession.Merge vermutlich ist, um zu genügen.

Andere Beiträge, die als Forschung verwendet werden:
Fluent NHibernate Newbie: Row wurde aktualisiert oder durch eine andere Transaktion gelöscht werden
Gibt es ein Alternative zu ISession.Merge (), die nicht bei optimistischer Sperrung ausgelöst wird?
StaleObjectstateException-Zeile wurde von aktualisiert oder gelöscht
Wie kann ich NHibernate zu speichern, nur geändert ändern Eigenschaften
Hibernate (JPA): Wie staleObjectStateException behandelt wird wenn mehrere Objekte geändert und festgelegt wurden (Java, aber relevant, denke ich)

    
BufferUnderrunOK 08.11.2011, 20:55
quelle

1 Antwort

2
  

Da die Benutzer den Besitz des übergeordneten Elements teilen, sollte Benutzer 1 erfolgreich speichern und das übergeordnete Element sowohl mit seinem neuen Kind als auch mit dem untergeordneten Element von Benutzer 2 speichern können.

Warum deaktivierst du nicht einfach die optimistische Sperre für die untergeordnete Sammlung? Dann kann jeder childs hinzufügen und es wird nicht die Version des Elternteils erhöhen.

Ansonsten ist hier die Lösung, die mein aktuelles Projekt für alle wiederherstellbaren Ausnahmen verwendet, die eine Sitzung auslösen könnte (z. B. Verbindung zur DB verloren, Fremdschlüssel verletzt, ...):

  1. Vor dem Aufruf von session.Flush() wird die Sitzung in MemoryStream serialisiert.
  2. Wenn session.Flush() oder transaction.Commit() eine wiederherstellbare Ausnahme auslöst, wird die ursprüngliche Sitzung entfernt und die gespeicherte Sitzung wird deserialisiert.
  3. Der aufrufende Bildschirm ruft dann die Information ab, dass die Sitzung nach einer Ausnahme wiederhergestellt wurde, und ruft dieselben Abfragen erneut auf, die beim ersten Öffnen des Bildschirms aufgerufen wurden. Und da sich alle geänderten Entitäten noch in der wiederhergestellten Sitzung befinden, hat der Benutzer jetzt den Status, unmittelbar bevor er Speichern drückt.
cremor 09.11.2011 08:28
quelle