L'objet ne peut pas être supprimé car il n'a pas été trouvé dans l'ObjectStateManager

114

J'obtiens cette erreur "L'objet ne peut pas être supprimé car il n'a pas été trouvé dans l'ObjectStateManager."

Mon code est:

    protected MyEntities sqlEntities;

    public virtual void Delete(TEntity entity)
    {
        System.Type t = typeof(TEntity);
        sqlEntities.DeleteObject(entity);
        sqlEntities.SaveChanges();
    }
Sami
la source
5
Désolé, il y a eu un problème dans le code selon lequel différents objets datacontext ont été utilisés pour récupérer et supprimer l'enregistrement.
Sami du
J'ai eu un bug comme celui-ci: var entity = new TEntity() { PK_ID = 23 }; sqlEntities.DeleteObject(entity);j'essayais de créer une entité fictive avec son PK correctement défini, dans l'espoir qu'Entity Framework appellerait DeleteObject basé sur le PK
C.Tewalt

Réponses:

156

Cela signifie que l'entité n'est pas attachée (elle n'a pas été chargée par la même instance de contexte). Essaye ça:

protected MyEntities sqlEntities;

public virtual void Delete(TEntity entity)
{
    sqlEntities.Attach(entity);
    sqlEntities.DeleteObject(entity);
    sqlEntities.SaveChanges();
}
Ladislav Mrnka
la source
1
Merci Ladislav. Cela m'a aidé lorsque j'ai eu deux contextes lors de la suppression des données enfant d'un parent - qui a été appelé à partir d'un contexte différent, en dehors d'un TransactionScope. Déplacement de l'appel parent dans la portée et dans le même contexte et mon problème a été résolu.
dan richardson
1
@Ladislav: Si j'utilise .Attach, j'obtiens une erreur différente: "Un objet entité ne peut pas être référencé par plusieurs instances de IEntityChangeTracker." Cela signifie-t-il qu'il existe déjà d'autres instances d'objet actives empêchant la suppression?
Matt
2
@Matt: Non cela signifie que votre objet est déjà attaché à un autre contexte. Il ne peut pas être attaché à deux en même temps. Vous devez utiliser le contexte d'origine pour supprimer l'entité.
Ladislav Mrnka
@Ladislav: Merci, cela m'a aidé à chercher plus loin - j'ai trouvé cette question qui semble répondre comment y répondre: est-il possible de vérifier si un objet est déjà attaché à un contexte de données dans Entity Framework? . Vous avez raison, cela semble être le problème dans mon cas. Merci pour la réponse rapide!
Matt
Merci pour le rappel, je l'avais joint lors de la mise à jour d'un enregistrement, mais pas lorsque je supprimais un enregistrement.
pinmonkeyiii
60

Juste une petite clarification sur la réponse de Ladislav Mrnka (qui devrait être la réponse acceptée).

Si comme moi, vous avez du code dans un format comme celui-ci:

using (var context = new MyDataContext())
{
    context.MyTableEntity.Remove(EntytyToRemove);
    var nrOfObjectsChanged = context.SaveChanges();
}

.. puis voici ce que vous voulez faire:

using (var context = new MyDataContext())
{
    // Note: Attatch to the entity:
    context.MyTableEntity.Attach(EntityToRemove);

    context.MyTableEntity.Remove(EntityToRemove);
    var nrOfObjectsChanged = context.SaveChanges();
}

Cela semble peut-être évident, mais il n'était pas clair pour moi au départ qu'il soit nécessaire de spécifier l' entité à attacher, et pas seulement le contexte .

Kjartan
la source
1
J'utilise cette approche et je reçois une exception: "L'instruction de mise à jour, d'insertion ou de suppression du magasin a affecté un nombre inattendu de lignes (0). Les entités peuvent avoir été modifiées ou supprimées depuis le chargement des entités. Actualiser les entrées ObjectStateManager."
kat1330 le
11

Juste pour propager l'explication des Kjartans:

J'ai eu:

public Project DeleteProject(int id)
    {
        using (var context = new Context())
        {
            var p = GetProject(id);
            context.Projects.Remove(p);
            context.SaveChanges();
            return p;
        }
    }

Le problème est que j'ai utilisé ma propre méthode (GetProject ()) pour obtenir l'entité (donc en utilisant un autre contexte pour charger l'entité):

public Project GetProject(int id)
    {
        using (var context = new Context())
        {
            var project = context.Projects
                .Include(p => p.Reports.Select(q => q.Issues.Select(r => r.Profession)))
                .Include(p => p.Reports.Select(q => q.Issues.Select(r => r.Room)))
                .SingleOrDefault(x => x.Id == id);
            return project;      
        }
    }

Une solution pourrait être d'attacher l'entité chargée comme l'indique Kjartan, une autre pourrait être la mienne solution, pour charger l'entité dans le même contexte:

public Project DeleteProject(int id)
    {
        using (var context = new Context())
        {
            var p = context.Projects.SingleOrDefault(x => x.Id == id);
            if (p == null)
                return p;

            context.Projects.Remove(p);
            context.SaveChanges();
            return p;
        }
    }
Esbenr
la source
2

Je sais que cette question est assez ancienne, mais aucune des réponses ci-dessus n'a fonctionné pour moi car je supprimais des registres de plus d'une classe / service et que chacun d'eux instanciait son propre contexte de connexion à la base de données.

Ce que j'ai fait pour le résoudre a été d'envoyer le premier contexte créé au reste des classes / services qui accèdent à la base de données.

Par exemple, serviceAj'allais supprimer certains de ses registres et appeler serviceBetserviceC faire de même avec leurs registres.

Je supprimerais alors mes registres sur serviceAet passerais en paramètre le contexte créé sur le serviceAto serviceBet serviceC.

Terkhos
la source
2

Vous pouvez écrire ce code:

var x=yourquery.FirstOrDefault(); 

sqlEntities.DeleteObject(x);
sqlEntities.SaveChanges();
mehdi
la source
1

Si aucune des solutions ci-dessus ne fonctionne, vous pouvez essayer cette solution:

context.Entry(yourObject).State = System.Data.Entity.EntityState.Deleted; context.SaveChanges();

Ala 'Alnajjar
la source
0

Vous devez être sûr que votre objet existe dans la liste que vous essayez de supprimer, vous devez mettre la condition suivante

if (context.entity.contains (votre objet))

retirez-le.

si vous avez une condition d'égalité compliquée, vous devez remplacer la méthode equal dans la classe d'entité pour mettre votre condition d'égalité, pour obtenir le bon chemin pour une méthode d'extension "entity.contains"

Mohammad Ahmad Abdullah
la source
0

Assurez-vous que le modèle passant dans Remove (Entity) est exactement le même que l'enregistrement de la base de données.

Parfois, il est possible de transmettre le modèle sans certains champs comme Id ou Date. conservez-les dans @ html.Hiddenfor si vous publiez sous forme de formulaire.

Le meilleur moyen est de transmettre l'ID et d'obtenir l'entité à l'aide de la méthode Find (Id) et de la transmettre à Remove (Entity)

J'espère que cela aide quelqu'un.

Dheeraj Palagiri
la source
0

J'ai ce problème et je le résous. Copiez simplement le code ci-dessous:

sqlEntities.Attach(entity);
sqlEntities.Remove(entity);
sqlEntities.SaveChanges();
vahid sabet
la source
0

Dans mon cas, il y avait un contexte, mais j'ai obtenu une entité avec l'option 'AsNoTracking'

vborutenko
la source