Ich rufe Filmdaten von einer externen API ab. In einer ersten Phase werde ich jeden Film kratzen und in meine eigene Datenbank einfügen. In einer zweiten Phase werde ich meine Datenbank regelmäßig aktualisieren, indem ich die API "Änderungen" der API verwende, die ich abfragen kann, um festzustellen, bei welchen Filmen die Informationen geändert wurden.
Meine ORM-Schicht ist Entity-Framework. Die Movie-Klasse sieht folgendermaßen aus:
class Movie
{
public virtual ICollection<Language> SpokenLanguages { get; set; }
public virtual ICollection<Genre> Genres { get; set; }
public virtual ICollection<Keyword> Keywords { get; set; }
}
Das Problem tritt auf, wenn ich einen Film habe, der aktualisiert werden muss: In meiner Datenbank wird das verfolgte Objekt und das neue, das ich vom Update-API-Aufruf erhalte, als unterschiedliche Objekte betrachtet, unabhängig davon .Equals()
.
Dies verursacht ein Problem, da beim Versuch, die Datenbank mit dem aktualisierten Film zu aktualisieren, diese eingefügt wird, anstatt den vorhandenen Film zu aktualisieren.
Ich hatte dieses Problem zuvor mit den Sprachen und meine Lösung bestand darin, nach den angehängten Sprachobjekten zu suchen, sie vom Kontext zu trennen, ihre PK in das aktualisierte Objekt zu verschieben und diese an den Kontext anzuhängen. Wenn SaveChanges()
es jetzt ausgeführt wird, wird es im Wesentlichen ersetzt.
Dies ist ein ziemlich stinkender Ansatz, denn wenn ich diesen Ansatz für mein Movie
Objekt fortsetze, bedeutet dies, dass ich den Film, die Sprachen, die Genres und die Schlüsselwörter trennen, jeden in der Datenbank nachschlagen, ihre IDs übertragen und die einfügen muss neue Objekte.
Gibt es eine Möglichkeit, dies eleganter zu tun? Im Idealfall möchte ich nur den aktualisierten Film an den Kontext übergeben und den richtigen Film auswählen, der basierend auf der Equals()
Methode aktualisiert werden soll, alle Felder aktualisieren und für jedes komplexe Objekt: Verwenden Sie den vorhandenen Datensatz erneut basierend auf seiner eigenen Equals()
Methode und fügen Sie if ein es existiert noch nicht.
Ich kann das Trennen / Anhängen überspringen, indem ich .Update()
Methoden für jedes komplexe Objekt bereitstelle , die ich in Kombination zum Abrufen aller angehängten Objekte verwenden kann. Dazu muss ich jedoch jedes einzelne vorhandene Objekt abrufen, um es dann zu aktualisieren.
quelle
id
und die Filme aus der externen API werden mithilfe des Felds mit den lokalen Filmen abgeglichentmdbid
. Ich kann nicht alle Entitäten abrufen, die in einem Aufruf aktualisiert werden müssen, da es sich um Filme, Genres, Sprachen, Schlüsselwörter usw. handelt. Jede dieser Entitäten hat eine PK und ist möglicherweise bereits in der Datenbank vorhanden.Antworten:
Ich habe nicht gefunden, was ich mir erhofft hatte, aber ich habe eine Verbesserung gegenüber der vorhandenen Sequenz zum Auswählen, Entfernen, Aktualisieren und Anhängen gefunden.
Mit der Erweiterungsmethode
AddOrUpdate(this DbSet)
können Sie genau das tun, was ich tun möchte: Einfügen, wenn es nicht vorhanden ist, und aktualisieren, wenn ein vorhandener Wert gefunden wurde. Ich habe es nicht früher bemerkt, da ich wirklich nur gesehen habe, dass es in derseed()
Methode in Kombination mit Migrationen verwendet wird. Wenn es einen Grund gibt, warum ich dies nicht verwenden sollte, lassen Sie es mich wissen.Beachten Sie Folgendes: Es ist eine Überlastung verfügbar, mit der Sie gezielt auswählen können, wie die Gleichheit bestimmt werden soll. Hier hätte ich meine verwenden können,
TMDbId
aber ich habe mich stattdessen dafür entschieden, meine eigene ID einfach ganz zu ignorieren und stattdessen eine PK auf TMDbId in Kombination mit zu verwendenDatabaseGeneratedOption.None
. Gegebenenfalls verwende ich diesen Ansatz auch für jede Untersammlung.Interessanter Teil der Quelle :
Auf diese Weise werden die Daten tatsächlich unter der Haube aktualisiert.
Alles, was übrig bleibt, ist,
AddOrUpdate
jedes Objekt aufzurufen , von dem ich betroffen sein möchte:Es ist nicht so sauber wie ich gehofft habe, da ich jedes Teil meines Objekts, das aktualisiert werden muss, manuell angeben muss, aber es ist ungefähr so nah wie es nur geht.
Verwandte Lektüre: /programming/15336248/entity-framework-5-updating-a-record
Aktualisieren:
Es stellte sich heraus, dass meine Tests nicht streng genug waren. Nachdem ich diese Technik angewendet hatte, bemerkte ich, dass die neue Sprache zwar hinzugefügt wurde, aber nicht mit dem Film verbunden war. in der Viele-zu-Viele-Tabelle. Dies ist ein bekanntes Problem mit scheinbar niedriger Priorität, das meines Wissens nicht behoben wurde.
Am Ende habe ich mich für den Ansatz entschieden, bei dem ich
Update(T)
Methoden für jeden Typ habe und diese Abfolge von Ereignissen befolge:Update()
Methode, um es mit den neuen Werten zu aktualisierenEs ist viel manuelle Arbeit und es ist hässlich, so dass es einige weitere Umgestaltungen durchlaufen wird, aber jetzt zeigen meine Tests, dass es für strengere Szenarien funktionieren sollte.
Nachdem ich es weiter aufgeräumt habe, verwende ich jetzt diese Methode:
Dadurch kann ich es so aufrufen und die zugrunde liegenden Sammlungen einfügen / aktualisieren:
Beachten Sie, wie ich den abgerufenen Wert dem ursprünglichen Stammobjekt neu zuordne: Jetzt ist er mit jedem angehängten Objekt verbunden. Das Aktualisieren des Stammobjekts (des Films) erfolgt auf folgende Weise:
quelle
Da Sie sich mit verschiedenen Feldern befassen
id
undtmbid
, schlage ich vor, die API zu aktualisieren, um einen einzigen und separaten Index aller Informationen wie Genres, Sprachen, Schlüsselwörter usw. zu erstellen. Rufen Sie dann auf, um Informationen zu indizieren und zu suchen, anstatt sie zu sammeln die gesamten Informationen zu einem bestimmten Objekt in Ihrer Movie-Klasse.quelle