Ich implementiere eine benutzerdefinierte NSIncrementalStore Unterklasse, die eine relationale Datenbank für persistenten Speicher verwendet. Eines der Dinge, mit denen ich immer noch zu kämpfen habe, ist die Unterstützung für optimistisches Sperren.Unterstützung für optimistisches Sperren in der Unterklasse NSIncrementalStore
(fühlen sich frei, diese lange Beschreibung Recht auf meine Frage unten überspringen) durch
ich analysiert, wie Core Data die SQLite inkrementelle Speicher nähert sich diesem Problem erzeugt durch die Untersuchung SQL-Protokolle und kam mit folgenden Ergebnissen:
Jede Entität Tabelle in der Datenbank hat eine Z_OPT c olumn, die angibt, wie oft eine bestimmte Instanz dieser Entität (Zeile) geändert wurde, beginnend mit 1 (anfängliche Einfügung).
Jedes Mal, wenn ein verwaltetes Objekt geändert wird, wird der Wert Z_OPT in der entsprechenden Datenbankzeile inkrementiert.
Der Speicher hält Cache (bezeichnet als Reihe Cache in Core Data-Dokumente) von NSIncrementalStoreNode Instanzen, die jeweils durch vorherige SELECT eine Version Eigenschaft gleich Z_OPT Wert zurückgekehrt oder UPDATE SQL-Abfrage in der Zeile des verwalteten Objekts.
Wenn ein verwaltetes Objekt aus NSManagedObjectContext (beispielsweise durch Ausführen von NSFetchRequest darauf) zurückgegeben wird, erstellt MOC snapshot dieses Objekt, das diese Version Nummer enthält.
Wenn das Objekt geändert oder gelöscht wird, stellt Core Data sicher, dass es nicht außerhalb des Kontexts geändert oder gelöscht wurde, indem es Versionen zwischengespeicherter Zeilen und Objekt-Snapshots vergleicht. All dies geschieht, wenn -save: für den Kontext aufgerufen wird, zu dem das Objekt gehört. Wenn die Versionen unterschiedlich sind, wird ein Zusammenführungskonflikt erkannt und basierend auf der Richtlinie zum Zusammenführen von Zusammenfassungen behandelt.
Wenn MOC gespeichert wird, die -newValuesForObjectWithID: withContext: Fehler: Verfahren wird für jeden geänderten/gelöschten Objekt aufgerufen, die wiederum kehrt NSIncrementalStoreNode mit der Versionsnummer. Diese Version wird dann mit der Snapshot-Version verglichen, und wenn sie unterschiedlich sind, schlägt das Speichern mit entsprechenden Zusammenführungskonflikten fehl (zumindest mit der standardmäßigen Zusammenführungsrichtlinie).
Dieser einfache Anwendungsfall funktioniert mit meinem Speicher seit -newValuesForObjectWithID: withContext: Fehler: prüft die Zeile Cache zuerst die ausreichend ist, wenn das Objekt gleichzeitig in anderem Kontext mit der gleichen Speicherinstanz geändert wurde. Wenn dies der Fall ist, enthält der Cache eine aktualisierte Zeile mit einer höheren Versionsnummer, die ausreicht, um einen Konflikt zu erkennen.
Aber wie kann ich erkennen, dass die zugrunde liegende Datenbank außerhalb meines Geschäfts geändert wurde, möglicherweise von einer anderen Anwendung oder einer anderen Geschäftsinstanz, die die gleiche Datenbankdatei verwendet? Ich weiß, dass dies ein selten auftretender Randfall ist, aber Core Data behandelt es richtig und ich würde es vorziehen, dasselbe zu tun.
Core-Data Store verwendet SQL-Abfragen, wie diese zu aktualisieren/löschen Zeile des Objekts:
UPDATE ZFOO SET Z_OPT=Y, (...) WHERE (...) AND Z_OPT=X
DELETE FROM ZFOO WHERE (...) AND Z_OPT=X
Wo:
X - Versionsnummer zuletzt in den Laden bekannt (aus dem Cache)
Y - Neue Versionsnummer
Wenn eine solche Abfrage fehlschlägt (keine betroffenen Zeilen), wird die Zeile im Cache des Speichers aktualisiert und die Version mit der zuvor zwischengespeicherten Version verglichen.
Meine Frage ist: Wie kann eine benutzerdefinierte NSIncrementalStore informieren Core Data, dass das optimistische Sperren Ausfall für einige aktualisiert/gelöscht/gesperrten Objekte aufgetreten ist? Es ist nur der Speicher, der in der Lage ist zu sagen, dass, wenn es behandelt NSSaveChangesRequest übergibt es -executeRequest: withContext: error: Methode.
Wenn die zugrunde liegende Datenbank unter dem Laden nicht ändert, dann sind Konflikte erkannt, da Core Data ruft -newValuesForObjectWithID: withContext: Fehler: auf jeden geändert werden/gelöscht/gesperrt Objekt vor der Ausführung speichern Änderungen auf dem Speicher fordern. Ich konnte keinen Weg für NSIncrementalStore finden, um Core Data zu informieren, dass ein optimistischer Sperrfehler aufgetreten ist nach begann es mit der Speicheranforderung umzugehen. Gibt es eine undokumentierte Möglichkeit, das zu tun? Core Data scheint einige Ausnahme in diesem Fall zu werfen, die dann magisch in fehlgeschlagene Speicheranforderung mit NSError übersetzt wird, die alle Konflikte auflistet. Ich kann das nur teilweise nachahmen, indem ich null von -executeRequest: withContext: error: zurückmelde und die Fehlermeldung selbst kreiere. Ich denke, dass es auch in diesem Szenario eine Möglichkeit geben muss, den standardmäßigen Kerndaten-Konfliktbehandlungsmechanismus zu verwenden.
Ich bin nicht wirklich darüber nachgedacht, wie genau das im CoreData-Framework implementiert ist. Aber ich weiß, wenn CoreData einen Merge-Konflikt meldet, besteht auch die Möglichkeit, dass der Merge-Konflikt das persistentSnapshot-Objekt enthält (das Objekt, wie es jetzt im Store existiert). Dies kann passieren, wenn der Koordinator die gespeicherten Daten in den Speicher schreibt und diese verändert findet. [siehe hier] (https://developer.apple.com/library/mac/#documentation/CoreData/Reference/NSMergeConflict_Class/Reference/Reference.html) –
@DanShelly: Vielen Dank für Ihren Kommentar. Sie haben völlig Recht, und das ist es, was ich in meiner inkrementellen Store-Implementierung zu erreichen versuche. Ich kann Zusammenführungskonflikte zwischen dem verwalteten Objektkontext und dem Zeilencache des persistenten Speichers erkennen und erzeugen (Fall 1 im Abschnitt "Überblick" des verknüpften Dokuments), aber nicht zwischen dem Zeilencache und dem externen Speicher (in meinem Fall rel. Datenbank). siehe Fall 2). Ich kann einen solchen Konflikt genau wie CoreDatas SQLite-Speicher erkennen, aber ich weiß nicht, wie ich diesen Konflikt an MOC melden soll, der gespeichert wird. –