对象无法被删除,因为在ObjectStateManager中未找到该对象。

118
我遇到了这个错误:"由于在ObjectStateManager中找不到该对象,因此无法删除该对象。"
我的代码如下:
    protected MyEntities sqlEntities;

    public virtual void Delete(TEntity entity)
    {
        System.Type t = typeof(TEntity);
        sqlEntities.DeleteObject(entity);
        sqlEntities.SaveChanges();
    }

6
抱歉,代码出了问题,导致获取和删除记录时使用了不同的数据上下文对象。 - Sami
我遇到了这样一个错误:var entity = new TEntity() { PK_ID = 23 }; sqlEntities.DeleteObject(entity); 我试图创建一个模拟实体,并正确设置其PK,希望Entity Framework可以根据PK调用DeleteObject。 - C. Tewalt
10个回答

161

这意味着实体未被关联(它没有由同一上下文实例加载)。请尝试以下方法:

protected MyEntities sqlEntities;

public virtual void Delete(TEntity entity)
{
    sqlEntities.Attach(entity);
    sqlEntities.DeleteObject(entity);
    sqlEntities.SaveChanges();
}

1
谢谢Ladislav。当我在从不同的上下文中调用删除父级数据的子级数据时,这对我很有帮助 - 在TransactionScope之外调用。将父级调用移动到范围内和相同的上下文中,我的问题得到了解决。 - dan richardson
1
@Ladislav:如果我使用.Attach,我会得到一个不同的错误:“实体对象不能被多个IEntityChangeTracker实例引用。”这是否意味着已经有其他对象实例存在,阻止了删除操作? - Matt
2
@Matt:这意味着你的对象已经附加到另一个上下文中。它不能同时附加到两个上下文中。你应该使用原始上下文来删除实体。 - Ladislav Mrnka
@Ladislav:谢谢你,这帮助我进一步寻找 - 我发现了这个问题,它似乎回答了如何解决它:在Entity Framework中检查对象是否已经附加到数据上下文是可能的吗?。你说得对,在我的情况下,这似乎是问题所在。感谢您的快速回答! - Matt
它起作用了。但是为什么?attach做了什么?我实际上并不理解“由同一上下文实例加载”的含义。 - Bugs Happen
显示剩余2条评论

66

关于Ladislav Mrnka的回答(应该是被接受的答案)只需要做出一个小的澄清。

如果和我一样,你的代码格式如下:

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

那么这就是你想要做的:

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

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

也许这似乎很明显,但最初我并不清楚需要指定要附加到的实体,而不仅仅是上下文


1
我使用这种方法并收到异常:“存储更新、插入或删除语句影响了意外数量的行(0)。实体可能已经在加载实体后被修改或删除。请刷新ObjectStateManager条目。” - kat1330

12

仅仅是为了分享Kjartans的解释:

我有:

public Project DeleteProject(int id)
    {
        using (var context = new Context())
        {
            var p = GetProject(id);
            context.Projects.Remove(p);
            context.SaveChanges();
            return p;
        }
    }
问题在于我使用了自己的方法(GetProject())来获取该实体(因此使用另一个上下文来加载实体):
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;      
        }
    }

一个解决方案可以是像Kjartan所说的那样附加已加载的实体,另一个可以是我的解决方案,在同一上下文中加载该实体:

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;
        }
    }

2
你可以编写以下代码:
var x=yourquery.FirstOrDefault(); 

sqlEntities.DeleteObject(x);
sqlEntities.SaveChanges();

2
如果以上方法都不起作用,您可以尝试以下解决方案: context.Entry(yourObject).State = System.Data.Entity.EntityState.Deleted; context.SaveChanges();
该代码将删除您的对象,并保存更改。

2
我知道这个问题很旧,但以上的解决方法对我都不起作用,因为我正在从多个类/服务中删除注册表,并且每个类/服务都实例化了自己的数据库连接上下文。
为了解决这个问题,我将第一个创建的上下文发送给将要访问数据库的其他类/服务。
例如,我的serviceA将删除其中一些注册表并调用serviceBserviceC以同样的方式处理它们的注册表。
然后,我会在serviceA上删除我的注册表,并将在serviceA上创建的上下文作为参数传递给serviceBserviceC

0

我遇到了这个问题并且解决了它。只需复制以下代码:

sqlEntities.Attach(entity);
sqlEntities.Remove(entity);
sqlEntities.SaveChanges();

0

在从列表中删除对象之前,请确保该对象存在, 您应该添加以下条件:

if(context.entity.contains(your object))

remove it.

如果您有一个复杂的相等条件,您应该重写实体类中的equal方法,以满足您的相等条件, 以获得正确的扩展方法 "entity.contains"


0
在我的情况下,有一个上下文,但我使用了 'AsNoTracking' 选项获取实体。

0
确保传递给Remove(Entity)的模型与数据库记录完全相同。
有时可能会省略一些字段(如Id或Date)而将模型传递。如果您要作为表单发布,请将其放入@html.Hiddenfor中。
最好的方法是传递ID并使用Find(Id)方法获取实体,然后将其传递给Remove(Entity)。
希望这能帮助到某些人。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接