我在使用新的实体框架Core 7ExecuteDelete
功能时遇到了一个奇怪的问题。
上下文:Bar
保存Foo
的列表(Bar
与Foo
的关系为1到n):
public class Bar
{
public int Id { get; set; }
public List<Foo> Foo { get; set; }
}
public class Foo
{
public int Id { get; set; }
public int BarId { get; set; }
public Bar bar { get; set; }
}
我叫
dbContext.Foo.Where(F => F.Id == <someID>).ExecuteDelete();
dbContext.SaveChanges();
然后在另一个类中(在这个调用之后),我运行:
Bar bar = dbContext.Bar
.Where(b => <condition>)
.Include(b => b.Foo)
.First();
在bar
中,先前呼叫中删除的Foo
仍然存在。dbContext
被定义为单例。
这些解决方案很好:
dbContext.Foo.Remove(<instanceFoo>);
dbContext.SaveChanges();
或
Bar.Foo.Remove(<instanceFoo>);
dbContext.Update(Bar);
dbContext.SaveChanges();
谁能给我解释一下发生了什么事?这可能是由于实体框架跟踪内存中实体变化的方式。
当执行dbContext.Foo.Where(F => F.Id == <someID>).ExecuteDelete()
时,EF将匹配的Foo
实体标记为已删除,并在调用dbContext.SaveChanges()
时将其从数据库中删除。但是,它不会从内存中删除已删除的实体。
当您随后查询Bar
实体并使用dbContext.Bar.Where(b => <condition>).Include(b => b.Foo).First()
包含其相关的Foo
实体时,EF从数据库中加载Bar
实体及其相关的Foo
实体并在内存中跟踪它们。由于EF在上一步中没有从内存中删除已删除的Foo实体,因此它仍然存在于Bar
对象的Foo
集合中。
你发布的替代解决方案之所以有效,是因为它们显式地从内存中删除了已删除的Foo
实体。
在第一种解决方案中,调用dbContext.Foo.Remove(<instanceFoo>)
将实体标记为已删除并将其从内存中删除,因此它不包括在后续查询中。
在第二种解决方案中,调用Bar.Foo.Remove(<instanceFoo>)
将实体从内存中Bar对象的Foo集合中删除,并将其标记为已修改,因此EF在调用dbContext.SaveChanges()
时将其从数据库中删除。
为了避免这个问题,可以在执行删除操作后从数据库中重新加载Bar
实体及其相关的Foo
实体,使用如下命令:
dbContext.Entry(bar).Collection(b => b.Foo).Load();
这将从db中为Bar
对象重新加载Foo
实体,并替换内存中的集合,因此已删除的Foo
实体将不再存在。