107 votes

Mettre à jour les liens lors de l’enregistrement des modifications d’objets EF4 POCO

Entity Framework 4, des objets POCO et ASP.Net MVC2. J'ai une relation plusieurs-à-plusieurs, disons entre le Blog et le Tag entités. Cela signifie que, dans mon T4 généré POCO article sur le Blog de la classe, j'ai:

public virtual ICollection<Tag> Tags {
    // getter and setter with the magic FixupCollection
}
private ICollection<Tag> _tags;

Je demande un billet de Blog et les Tags à partir d'une instance de la ObjectContext et de l'envoyer à une autre couche (Afficher dans l'application MVC). Plus tard, j'ai récupérer la mise à jour du Blog avec les modifications des propriétés et changé relations. Par exemple, il avait des balises "A", "B" et "C", et les nouvelles balises sont des "C" et "D". Dans mon exemple il n'y a pas de nouveaux Tags et les propriétés des Balises de ne jamais changer, de sorte que la seule chose qui doit être enregistré est l'évolution des relations. Maintenant, j'ai besoin de le sauvegarder dans un autre ObjectContext. (Mise à jour: Maintenant, j'ai essayé de faire dans le même contexte d'instance et aussi échoué.)

Le problème: je ne peux pas le faire enregistrer les relations correctement. J'ai essayé tout ce que j'ai trouvé:

  • Le contrôleur.UpdateModel et de Contrôleur.Tryupdatemodel pour mettre ne fonctionnent pas.
  • L'obtention de l'ancien article sur le Blog à partir du contexte puis de modifier la collection ne fonctionne pas. (avec les différentes méthodes de le point suivant)
  • Ce serait probablement le travail, mais j'espère que c'est juste une solution de contournement, pas de la solution :(.
  • Essayé de Joindre/Ajouter/ChangeObjectState fonctions pour l'article sur le Blog et/ou des Balises dans toutes les combinaisons possibles. A échoué.
  • Cela ressemble à ce que j'ai besoin, mais il ne fonctionne pas (j'ai essayé de réparer, mais ne peut pas pour mon problème).
  • Essayé ChangeState/Ajouter/Attach/... la relation des objets du contexte. A échoué.

"Ne pas travailler" signifie dans la plupart des cas que j'ai travaillé sur la "solution" jusqu'à ce qu'il ne produit pas d'erreurs et permet d'économiser au moins les propriétés de billet de Blog. Ce qui se passe avec les relations varie: habituellement, les Balises sont ajoutés de nouveau à la Balise table avec le nouveau PKs et sauvés article sur le Blog des références à ceux-ci et pas celle d'origine. Bien sûr, le retour des Balises PKs, et avant de les enregistrer/mettre à jour les méthodes d'-je vérifier le PKs et ils sont égaux à ceux de la base de données donc probablement EF pense qu'ils sont de nouveaux objets, et de les PKs sont le temp.

Un problème que je connaissent et qui peuvent faire qu'il est impossible de trouver un système automatisé de solution simple: Lorsqu'un POCO de l'objet de collection est modifié, ce qui devrait passer par le ci-dessus mentionné collection virtuelle de la propriété, parce qu'alors la FixupCollection astuce permettra de mettre à jour l'inverse des références sur l'autre extrémité de la plusieurs-à-plusieurs relations. Cependant lors d'un point de Vue "renvoie" une mise à jour du Blog de l'objet, qui n'est pas arrivé. Cela signifie que peut-être il n'y a pas de solution simple à mon problème, mais ça me ferait vraiment très triste et je déteste le EF4-POCO-MVC triomphe :(. Aussi, cela voudrait dire que EF ne peut pas le faire dans le MVC de l'environnement selon la EF4 types d'objets sont utilisés :(. Je pense que l'instantané en fonction de suivi des modifications devraient savoir que le changement de billet de Blog a des relations, à des Étiquettes existantes PKs.

Btw: je pense que le même problème se produit avec un-à-plusieurs relations (google et mon collègue le dire). Je vais l'essayer à la maison, mais même si c'œuvres qui ne m'aide pas dans mes six plusieurs-à-plusieurs liens dans mon application :(.

143voto

Ladislav Mrnka Points 218632

Nous allons essayer de cette façon:

  • Joindre article sur le Blog de contexte. Après la fixation de l'objet et contexte de l'état de l'objet, tous les objets et toutes les relations est fixé à Inchangé.
  • Contexte d'utilisation.Vousmanager.ChangeObjectState à définir votre billet de Blog à Modifié
  • Itérer sur le Tag de la collection
  • Contexte d'utilisation.Vousmanager.ChangeRelationshipState pour définir l'état de la relation entre le courant et la Balise de billet de Blog.
  • SaveChanges

Edit:

Je suppose que l'un de mes commentaires vous a donné un faux espoir que les EF fera la fusion pour vous. J'ai beaucoup joué à ce problème et à ma conclusion, dit-EF va pas le faire pour vous. Je pense que vous avez aussi trouvé ma question sur MSDN. En réalité, il ya beaucoup de ces questions sur Internet. Le problème est qu'il n'est pas clairement indiqué comment faire face à ce scénario. Donc permet de jeter un oeil sur le problème:

Problème de fond

L'EF doit suivre les changements sur les entités afin de persistance sait quels documents doivent être mis à jour, insérées ou supprimées. Le problème est qu'il est ObjectContext responsabilité de faire le suivi des changements. ObjectContext est en mesure de suivre les changements uniquement pour les attachés entités. Les entités qui sont créés à l'extérieur de la ObjectContext ne sont pas suivis du tout.

La description du problème

Basé sur la description ci-dessus, nous pouvons clairement affirmer que EF est plus approprié pour les scénarios où l'entité est toujours lié à un contexte typique pour l'application WinForm. Les applications Web nécessite déconnecté scénario où le contexte est fermé après le traitement de la demande et de l'entité de contenu est transmis en réponse HTTP au client. Demande HTTP suivante fournit modifié le contenu de l'entité qui doit être recréé, attaché au nouveau contexte et a persisté. Les loisirs se fait généralement en dehors du contexte de la portée (architecture en couches avec persistance ignorace).

Solution

Alors, comment faire face à de tels scénario déconnecté? Lors de l'utilisation de classes POCO nous avons 3 façons de composer avec le suivi des modifications:

  • Instantané nécessite même contexte = inutile pour le scénario déconnecté
  • Suivi dynamique des procurations - nécessite même contexte = inutile pour le scénario déconnecté
  • La synchronisation manuelle.

La synchronisation manuelle sur une seule entité est une tâche facile. Vous avez juste besoin d'attacher de l'entité et de l'appel AddObject pour l'insertion, DeleteObject pour la suppression ou l'ensemble de l'état dans Vousmanager Modifié pour la mise à jour. La vraie douleur vient quand vous avez à traiter avec le graphe d'objet au lieu d'une seule entité. Cette douleur est pire encore, lorsque vous avez à traiter avec des associations indépendantes (ceux qui n'utilisent pas de Clé Étrangère à la propriété) et de nombreuses de nombreuses relations. Dans ce cas, vous devez synchroniser manuellement chaque entité dans le graphe d'objet, mais aussi chaque relation dans le graphe d'objet.

La synchronisation manuelle est proposée comme solution par la documentation MSDN: l'attachement et le Détachement des objets dit:

Les objets sont liées à l'objet contexte dans un état Inchangé. Si vous besoin de changer l'état d'un objet ou la relation parce que vous savez que votre objet a été modifié en état détaché, utilisez l'une des méthodes suivantes.

Les moyens mentionnés sont ChangeObjectState et ChangeRelationshipState de Vousmanager = manuel de suivi des modifications. Proposition similaire dans d'autres documentation MSDN article: la Définition et la Gestion des Relations dit:

Si vous travaillez avec déconnecté les objets que vous devez gérer manuellement la la synchronisation.

En outre, il est post de blog liées à EF v1 qui critiquent exactement ce comportement de EF.

Raison pour solution

EF a beaucoup de "utiles", les opérations et les paramètres comme l'Actualisation, de la Charge, ApplyCurrentValues, ApplyOriginalValues, MergeOption etc. Mais par mon enquête, toutes ces fonctionnalités sont uniquement pour une seule entité et affecte uniquement les scalaires preperties (= pas de navigation propriétés et relations). Je préfère ne pas tester cette méthode avec les types complexes imbriquées au sein de l'entité.

Autre solution proposée

Au lieu de véritable fonctionnalité de Fusion EF équipe fournit quelque chose qui s'appelle l'Autonomie des Entités de Suivi (STE) qui ne permettent pas de résoudre le problème. Tout d'abord STE fonctionne uniquement si l'exemple est utilisé pour l'ensemble du traitement. Dans l'application web, il n'est pas le cas, à moins que vous stockez instance en vue de l'état ou de la session. En raison que je suis très malheureux de l'utilisation de EF et je vais vérifier les caractéristiques de NHibernate. Première observation dit que NHibernate a peut-être une telle fonctionnalité.

Conclusion

Je vais mettre fin à cette hypothèses avec un seul lien à l'autre liés à la question sur le forum MSDN. Vérifier Zeeshan Hirani de réponse. Il est l'auteur de Entity Framework 4.0 Recettes. Si il dit que la fusion des graphes d'objets n'est pas pris en charge, je crois en lui.

Mais il y a quand même possibilité que je suis complètement faux et certaines automatique de la fonctionnalité de fusion existe en EF.

Edit 2:

Comme vous pouvez le voir cela a déjà été ajouté MME se Connecter en tant que suggestion en 2007. MS a fermé comme quelque chose à faire dans la prochaine version, mais en fait rien n'avait été fait pour remédier à cette lacune, à l'exception de STE.

19voto

brentmckendrick Points 1538

J'ai une solution pour le problème qui a été décrit ci-dessus par Ladislav. J'ai créé une méthode d'extension pour le DbContext qui va automatiquement effectuer les ajouter/mettre à jour/supprimer est basé sur un diff de la condition graphique et a persisté graphique.

À l'heure actuelle à l'aide de Entity Framework, vous devez effectuer les mises à jour des contacts manuellement, vérifier si chaque contact est nouveau et d'ajouter, de vérifier si la mise à jour et de modifier, vérifier s'il a été retiré puis le supprimer de la base de données. Une fois que vous avez à faire pour quelques différents agrégats dans un grand système, vous commencez à réaliser il doit y avoir une meilleure façon générique.

Veuillez prendre un coup d'oeil et voir si cela peut aider http://refactorthis.wordpress.com/2012/12/11/introducing-graphdiff-for-entity-framework-code-first-allowing-automated-updates-of-a-graph-of-detached-entities/

Vous pouvez aller directement à la code ici https://github.com/refactorthis/GraphDiff

9voto

c0y0teX Points 473

Je sais que c'est la fin de l'OP, mais puisque c'est un problème très commun que j'ai posté dans ce cas, il sert à quelqu'un d'autre. J'ai pensé autour de cette question et je crois que j'ai une solution plutôt simple, ce que je fais, c'est:

  1. Économiser de l'objet (les Blogs par exemple), d'en définir l'état Modifié.
  2. Interroger la base de données pour la mise à jour de l'objet, y compris les collections j'ai besoin de mettre à jour.
  3. Requête et de les convertir .ToList() les entités que je veux faire de ma collection pour y inclure.
  4. Mise à jour de l'objet principal de la collection(s) à la Liste que j'ai obtenu à partir de l'étape 3.
  5. SaveChanges();

Dans l'exemple suivant "dataobj" et "_categories" sont les paramètres reçus par mon contrôleur "dataobj" est mon principal objet, et "_categories" est un IEnumerable contenant les Id des catégories de l'utilisateur sélectionné dans la vue.

    db.Entry(dataobj).State = EntityState.Modified;
    db.SaveChanges();
    dataobj = db.ServiceTypes.Include(x => x.Categories).Single(x => x.Id == dataobj.Id);
    var it = _categories != null ? db.Categories.Where(x => _categories.Contains(x.Id)).ToList() : null;
    dataobj.Categories = it;
    db.SaveChanges();

Il fonctionne même pour les multiples relations

7voto

Eric J. Points 73338

L'équipe d'Entity Framework est conscient que c'est un problème de convivialité et plans à l'adresse de post-EF6.

À partir de l'Entité Cadre de l'équipe:

C'est un problème de convivialité que nous en sommes conscients et c'est quelque chose que nous avons pensé et d'un plan pour faire plus de travail sur le post-EF6. J'ai créé cet élément de travail de suivre la question: http://entityframework.codeplex.com/workitem/864 L'élément de travail contient également un lien vers la voix utilisateur de ce--je vous encourage à voter pour elle si vous ne l'avez pas déjà fait.

Si cela vous concerne, à voter pour la fonction d'

http://entityframework.codeplex.com/workitem/864

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X