59 votes

Un objet avec la même clé existe déjà dans l'ObjectStateManager. L'ObjectStateManager ne peut pas suivre plusieurs objets avec la même clé.

En gros, j'ai une table qui contient quelques propriétés pour une entreprise. Il s'agit de la table "maître" et leur ID est utilisé dans de nombreuses autres tables. Je trouve essentiellement leur ID par cette méthode :

private Company currentcompany()
    {
        Company cuco = db.Companies.Single(x => x.username == User.Identity.Name);
        return cuco;
    }

Je dois donner aux utilisateurs la possibilité de mettre à jour divers détails les concernant stockés dans cette table, ce que j'ai fait parfaitement bien - cependant, j'ai remarqué une grande faille de sécurité !

En utilisant Tamper Data sur Firefox (et j'imagine Fidler/beaucoup d'autres), je pourrais facilement changer l'ID caché et modifier les détails d'une autre entreprise.

Pour arrêter cela, j'ai ajouté les lignes suivantes à l'action modify :

        Company cuco = currentcompany();

        if (company.id != cuco.id)
        {
            return Content("Security Error");
        }

(FYI - Company est un modèle/POCO représentant une entreprise, et company lui-même est la donnée du formulaire).

Après avoir ajouté ceci, si je modifie l'ID dans les données du formulaire, cela fonctionne comme prévu et fait apparaître "Security Error", cependant, s'il n'y a pas d'erreur et que je continue, j'obtiens l'erreur dans la question.

"Un objet avec la même clé existe déjà dans l'ObjectStateManager. L'ObjectStateManager ne peut pas suivre plusieurs objets avec la même clé."

Je pense que c'est parce que EF détecte et conserve la première extraction de données, mais je ne suis pas sûr de la façon de corriger cela.

Des conseils ?

edit- -mise à jour-

Si vous comprenez ce que j'essaie de faire, y a-t-il un meilleur moyen de contourner ce problème ?

150voto

Ladislav Mrnka Points 218632

Si vous chargez l'entité à partir du contexte, vous ne pouvez plus attacher une entité avec la même clé. La première entité est toujours conservée dans le cache interne du contexte et le contexte ne peut contenir qu'une seule instance avec une valeur de clé donnée par type (c'est ce qu'on appelle la carte d'identité et la carte d'identité). Je l'ai décrit ici dans une autre situation).

Vous pouvez résoudre ce problème en détachant l'ancienne instance mais vous n'êtes pas obligé de le faire. Si vous avez seulement besoin de sauvegarder de nouvelles valeurs, vous pouvez utiliser ceci :

  • API ObjectContext : context.YourEntitySet.ApplyCurrentValues(newEntity);
  • API DbContext : context.Entry(oldEntity).CurrentValues.SetValues(newEntity);

5voto

Serj Sagan Points 2731

Juste un peu d'aide pour vous si vous ne savez pas comment trouver la oldEntity d'après Ladislav :

var entityKey = context.NewEntitySet.Create().GetType().GetProperty("Id").GetValue(newEntity);

factory.Entry(context.Set<NewEntityType>().Find(entityKey)).CurrentValues.SetValues(newEntity);

1voto

Sandeep Points 712

Comme l'indique clairement l'erreur - vous devez vous assurer de obtenir des données existantes et modifier les données de cet élément avec les informations mises à jour et enregistrez-la. Vous ne rencontrerez pas ce problème si vous mettez à jour des informations très spécifiques et non des informations clés telles que ID et ID de la clé étrangère
Le code ci-dessous devrait faire l'affaire !

 public virtual void Update(TEntity entity)
    {
        _context.Entry(entity).State = EntityState.Modified;

        this._context.SaveChanges();
    }

L'utilisation sera -

 public async Task<bool> UpdateVendorItem(string userId, Vendor modified)
    {

        try
        {
            var existing = await this.GetVendors().SingleOrDefaultAsync(a => a.Id == modified.Id);

            //Set updated info
            existing.VendorName = modified.VendorName;

            //Update address information
            existing.Address.AddressLine1 = modified.Address.AddressLine1;
            ... 

            await _vendorRepository.UpdateAsync(existing);

    ...

0voto

Steve Mallory Points 2713

J'ai vu ce numéro hier. Vous devez détacher cuco avant la mise à jour. Vérifiez la solution dans ce réponse

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