1
votes

L'entité ne se met pas à jour à l'aide de l'approche Code-First

J'ai cette classe que j'utilise pour les opérations DB:

public class EntityService<TEntity> : IRepository<TEntity> where TEntity : BaseModel
{

     ApplicationDbContext _context;
     private DbSet<TEntity> _entities;

     public EntityService()
     {
         _context = new ApplicationDbContext();
     }

     public virtual void Update(TEntity entity)
     {
          if (entity == null)
               throw new ArgumentNullException(nameof(entity));

          try
          {
                var dbEnt = _context.Set<TEntity>().Where(c => c.Id == entity.Id).First();

                dbEnt = entity;
                dbEnt.UpdatedBy = GetCurrentUser();
                dbEnt.DateUpdated = DateTime.Now;
                _context.SaveChanges();
           }
           catch (DbUpdateException exception)
           {
                throw new Exception(GetFullErrorTextAndRollbackEntityChanges(exception), exception);
           }

           //-----other methods for insert and get working fine----
}

Il existe également d'autres méthodes dans cette classe pour insert et get code > fonctionnent bien. Seule cette méthode de mise à jour ne met pas à jour l'entité et ne lève pas l'exception. p> p> Méthode Add () ajoutant des lignes en double pour les modèles liés dans Code-First Entity Framework p >

Je pense que ces deux éléments ont la même raison de suivre les modifications. Mais l'un ajoute que l'autre ne se met pas à jour.


14 commentaires

si vous voulez mettre à jour entity pourquoi vous l'avez à nouveau et mettez entity dans dbEnt ?


@ hassan.ef Cela ne fonctionnait pas quand j'ai fait ça. J'ai donc pensé que si je récupérais l'entité de base de données à partir de la base de données, _context aurait sa référence db. Parce que entity n'est qu'un paramètre de méthode.


Je pense qu'au début, vous devriez mettre à jour le paramètre de dbEnt avec le paramètre de entity comme ceci: par exemple: edbEnt.Name = entity.Name et dont utilisez dbEnt = entity .


@ hassan.ef C'est une méthode courante pour mettre à jour toutes les entités. Je ne peux pas attribuer explicitement une propriété individuelle ici.


Avant d'enregistrer, vous pouvez essayer d'appeler context.Entry (entity) .State = EntityState.Modified; .


@bolkay Je l'ai essayé mais cela me montre cette erreur L'association d'une entité de type '' a échoué car une autre entité du même type a déjà la même valeur de clé primaire.Cela peut se produire lors de l'utilisation de la méthode 'Attach' ou en définissant l'état d'une entité sur «Inchangé» ou «Modifié» si des entités du graphique ont des valeurs de clé en conflit. `


@bolkay suite du commentaire ci-dessus Cela peut être dû au fait que certaines entités sont nouvelles et n'ont pas encore reçu les valeurs de clé générées par la base de données.Dans ce cas, utilisez la méthode 'Add' ou l'état d'entité 'Added' pour suivre le graphique, puis définissez l'état des entités non nouvelles sur "Inchangé" ou "Modifié" selon le cas. "


@bolkay Comment utiliser la solution suggérée par l'erreur?


Il s'agit d'un problème de suivi. Essayez quelque chose comme: _context.Set () .Add (dbEnt); Then: _context.Entry (dbEnt) .State = EntityState.Modified; Then SaveChanges (); Vous pouvez consulter les différents états des entités.


@bolkay Après cela, il affiche cette erreur L'enregistrement ou l'acceptation des modifications a échoué car plus d'une entité de type '' a la même valeur de clé primaire. Assurez-vous que les valeurs de clé primaire explicitement définies sont uniques. Assurez-vous que les clés primaires générées par la base de données sont correctement configurées dans la base de données et dans le modèle Entity Framework.


Votre code publié est correct sauf pour dbEnt = entity; . À la place, copiez les champs modifiés dans dbEnt.Prop1 = entity.Prop1; etc. Nous utilisons Automapper pour faire tous les champs en 1 étape: Mapper.Map (entity, dbEnt);


Vous avez commencé une prime parce que la question n'a pas reçu suffisamment d'attention. Mais qu'est-ce qui n'est pas clair / suffisant dans ma réponse? Je pense avoir décrit exactement la cause de l'échec de la mise à jour. Et proposé des alternatives de travail. Il vous serait beaucoup moins coûteux de répondre à ma réponse si elle n’était pas assez claire.


@GertArnold J'ai déjà essayé votre réponse comme je l'ai mentionné dans les commentaires précédents. Bolkay m'a suggéré ceci. J'ai essayé par tous les moyens d'attacher l'entité au suivi des modifications mais cela ne fonctionne pas. Même j'ai essayé d'initialiser à nouveau l'objet ApplicationContext dans cette méthode. Mais j'essaie toujours d'autres moyens et je cherche d'autres suggestions afin de pouvoir obtenir un indice. C'est pourquoi j'ai commencé la prime. Je reviendrai sur votre réponse une fois que j'aurai essayé tout ce que je peux trouver sur Internet.


Ce que vous avez essayé exactement n'est pas clair, car il ne s'agit que d'un fragment dans un commentaire. Mais quoi qu'il en soit, cela n'a pas de sens d'utiliser Add () pour une entité que vous venez d'extraire de la base de données et que vous souhaitez uniquement mettre à jour. C'est déjà attaché au contexte! Il vous suffit de mettre à jour ses valeurs et d'enregistrer les modifications. C'est l'essentiel de ma deuxième suggestion.


4 Réponses :


4
votes

La ligne ...

dbEnt = entity;

... attache un objet entité au contexte et renvoie une référence à cette entité.

Puis la ligne ...

var dbEnt = _context.Set<TEntity>().Where(c => c.Id == entity.Id).First();

... remplace cette référence par une référence à la variable entity qui entre dans la méthode. Ce n'est pas l'objet d'entité suivi . Vous avez pratiquement perdu la référence à l'entité suivie et il est impossible de la modifier plus longtemps.

Vous devez soit attacher entity au contexte et la marquer comme modifiée, soit obtenir dbEnt comme vous le faites déjà et modifiez et enregistrez cet objet. Les deux méthodes ont des avantages et des inconvénients, voir ici .


0 commentaires

1
votes

Après avoir récupéré l'entité de _context, mettez à jour tous les champs à partir du nouveau détail et définissez l'état de l'entité sur

var dbEnt = _context.Set<TEntity>().Where(c => c.Id == entity.Id).First();
dbEnt.Name = entity.Name;
...
...
...
dbEnt.UpdatedBy = GetCurrentUser();
dbEnt.DateUpdated = DateTime.Now;
_context.Entry(dbEnt).State = EntityState.Modified;
_context.SaveChanges();

modifié


1 commentaires

La configuration de State = EntityState.Modified n'est pas nécessaire, je dirais même pas recommandée.



0
votes

Si vous avez trouvé votre entité via l'ID

dbEnt = entity;

, pourquoi cette ligne?

var dbEnt = _context.Set<TEntity>().Where(c => c.Id == entity.Id).First();

Supprimez la ligne ci-dessus car elle supprimera la référence à l'objet suivi.


0 commentaires

-1
votes

Merci à tous. J'ai reçu beaucoup d'indices de vos réponses. Comme @GertArnold et La réponse de @Colonel Software m'a laissé entendre que j'avais modifié mon code comme ceci et cela a fonctionné:

//Assigning BaseModel properties
entity.CreatedBy = dbEnt.CreatedBy;
entity.UpdatedBy = GetCurrentUser();
entity.DateUpdated = DateTime.Now;
entity.DateCreated = dbEnt.DateCreated;

//Changing entity states
_context.Entry(dbEnt).State = EntityState.Detached;
_context.Entry(entity).State = EntityState.Modified;

_context.SaveChanges();


11 commentaires

Pourquoi ne pas simplement modifier et enregistrer dbEnt comme cela se produit dans la réponse à laquelle vous faites référence?


@GertArnold J'ai essayé, mais il affiche une exception de valeurs de clé primaire en double. J'essaierai de résoudre ce problème plus tard, mais pour l'instant cela fonctionne et je dois livrer le projet bientôt, je dois donc opter pour cette solution. Merci pour votre aide J'ai beaucoup appris de votre réponse et de +1.


Downvote pour quoi? Cela résout mon problème. Ce n'est peut-être pas la meilleure solution, mais c'est une solution.


Ensuite, vous avez fait plus que ce que vous montrez. Comme dit dans une autre réponse (et la mienne), tout ce dont vous avez besoin est de supprimer dbEnt = entity; .


Le vote défavorable est que votre code fonctionne à peine . Utiliser AddOrUpdate ici est assez étrange.


@GertArnold La solution était un mélange de toutes les réponses. Il y avait deux problèmes. L'un d'entre vous a mentionné que c'était parce que le suivi avait été perdu et une autre mention par Colonel Software suggérait que je devais attribuer toutes les propriétés du modèle de base.


@GertArnold de toute façon, je marque votre réponse au lieu de la mienne. Veuillez retirer le vote défavorable car c'est l'une des solutions.


Désolé, mon vote défavorable est valable, car c'est une mauvaise méthode pour mettre à jour une entité. Vous ne devriez vraiment pas utiliser AddOrUpdate tant que vous ne savez pas ce qu'il fait (et ne le fait pas ). Et quand vous comprenez que vous ne l'utiliserez plus.


@GertArnold Ok alors pouvez-vous m'expliquer ou me renvoyer un lien où je peux trouver ses inconvénients? Et pouvez-vous s'il vous plaît modifier mon code et mettre à jour votre réponse?


@GertArnold D'une manière ou d'une autre, j'ai pu le faire sans AddOrUpdate . Veuillez vérifier la mise à jour de ma réponse et retirer le vote négatif si cela vous convient.


Supprimez dbEnt = entity; et modifiez les propriétés de dbEnt !