Je récupère les données du film à partir d'une API externe. Dans une première phase, je vais gratter chaque film et l'insérer dans ma propre base de données. Dans une deuxième phase, je mettrai périodiquement à jour ma base de données en utilisant l'API "Changes" de l'API que je peux interroger pour voir quels films ont vu leurs informations modifiées.
Ma couche ORM est Entity-Framework. La classe Movie ressemble à ceci:
class Movie
{
public virtual ICollection<Language> SpokenLanguages { get; set; }
public virtual ICollection<Genre> Genres { get; set; }
public virtual ICollection<Keyword> Keywords { get; set; }
}
Le problème se pose lorsque j'ai un film qui doit être mis à jour: ma base de données considérera l'objet suivi et le nouveau que je reçois de l'appel de l'API de mise à jour comme des objets différents, sans tenir compte .Equals()
.
Cela provoque un problème car lorsque j'essaie maintenant de mettre à jour la base de données avec le film mis à jour, il l'insérera au lieu de mettre à jour le film existant.
J'ai déjà rencontré ce problème avec les langues et ma solution était de rechercher les objets de langage attachés, de les détacher du contexte, de déplacer leur PK vers l'objet mis à jour et de l'attacher au contexte. Une SaveChanges()
fois exécuté, il le remplacera essentiellement.
C'est une approche plutôt malodorante car si je continue cette approche de mon Movie
objet, cela signifie que je devrai détacher le film, les langues, les genres et les mots-clés, rechercher chacun dans la base de données, transférer leurs identifiants et insérer le de nouveaux objets.
Existe-t-il un moyen de le faire plus élégamment? Idéalement, je veux simplement passer le film mis à jour au contexte et sélectionner le film correct à mettre à jour en fonction de la Equals()
méthode, mettre à jour tous ses champs et pour chaque objet complexe: utiliser à nouveau l'enregistrement existant en fonction de sa propre Equals()
méthode et insérer si il n'existe pas encore.
Je peux ignorer le détachement / attachement en fournissant des .Update()
méthodes sur chaque objet complexe que je peux utiliser en combinaison avec la récupération de tous les objets attachés, mais cela me demandera toujours de récupérer chaque objet existant pour le mettre à jour.
la source
id
et les films de l'API externe sont mis en correspondance avec ceux locaux en utilisant le champtmdbid
. Je ne peux pas récupérer toutes les entités qui doivent être mises à jour en un seul appel, car il s'agit de films, de genres, de langues, de mots clés, etc. Chacun d'eux a un PK et peut déjà exister dans la base de données.Réponses:
Je n'ai pas trouvé ce que j'espérais mais j'ai trouvé une amélioration par rapport à la séquence existante select-detach-update-attach.
La méthode d'extension
AddOrUpdate(this DbSet)
vous permet de faire exactement ce que je veux faire: insérer si elle n'est pas là et mettre à jour si elle a trouvé une valeur existante. Je ne me suis pas rendu compte que j'utilisais cela plus tôt car je ne l'ai vraiment vu que utilisé dans laseed()
méthode en combinaison avec Migrations. S'il y a une raison pour laquelle je ne devrais pas l'utiliser, faites le moi savoir.Quelque chose d'utile à noter: il y a une surcharge disponible qui vous permet de sélectionner spécifiquement comment l'égalité doit être déterminée. Ici, j'aurais pu utiliser mon,
TMDbId
mais j'ai plutôt choisi de simplement ignorer mon propre ID et d'utiliser plutôt un PK sur TMDbId combiné avecDatabaseGeneratedOption.None
. J'utilise également cette approche sur chaque sous-collection, le cas échéant.Partie intéressante de la source :
c'est ainsi que les données sont réellement mises à jour sous le capot.
Tout ce qui reste est d'appeler
AddOrUpdate
chaque objet que je veux être affecté par ceci:Ce n'est pas aussi propre que je l'espérais car je dois spécifier manuellement chaque partie de mon objet qui doit être mise à jour, mais c'est à peu près aussi proche que possible.
Lecture connexe: /programming/15336248/entity-framework-5-updating-a-record
Mise à jour:
Il s'avère que mes tests n'étaient pas assez rigoureux. Après avoir utilisé cette technique, j'ai remarqué que bien que la nouvelle langue ait été ajoutée, elle n'était pas connectée au film. dans le tableau plusieurs-à-plusieurs. Il s'agit d' un problème connu mais apparemment de faible priorité et qui n'a pas été résolu pour autant que je sache.
Au final, j'ai décidé d'opter pour l'approche où j'ai des
Update(T)
méthodes sur chaque type et de suivre cette séquence d'événements:Update()
méthode pour le mettre à jour avec les nouvelles valeursC'est beaucoup de travail manuel et c'est moche donc ça va passer par quelques refactorings de plus, mais maintenant mes tests indiquent que cela devrait fonctionner pour des scénarios plus rigoureux.
Après l'avoir nettoyé davantage, j'utilise maintenant cette méthode:
Cela me permet de l'appeler ainsi et d'insérer / mettre à jour les collections sous-jacentes:
Remarquez comment je réaffecte la valeur récupérée à l'objet racine d'origine: maintenant elle est connectée à chaque objet attaché. La mise à jour de l'objet racine (le film) se fait de la même manière:
la source
Étant donné que vous traitez avec différents domaines
id
ettmbid
, je suggère que la mise à jour de l'API pour faire un index unique et séparé de toutes les informations comme les genres, les langues, les mots clés, etc. toutes les informations sur un objet spécifique dans votre classe Movie.la source