无法删除在EntityFramework Core中拥有实体的实体

时间:2019-05-08 13:39:11

标签: c# entity-framework-core

我有以下实体:

public class Employee
{
    public Guid Id { get; set; }
    public string Name { get; set; }
    public Address Address { get; set; }
}
public class Address
{
    public string City { get; set; }
    public string State { get; set; }
}

使用流畅的API,我将拥有的实体配置如下:

 private void ConfigureEmployee(EntityTypeBuilder<Employee> builder)
{
    builder.OwnsOne(x => x.Address, w =>
    {
        w.Property(x => x.City).HasMaxLength(100);
        w.Property(x => x.State).HasMaxLength(100);
    });
}

当我尝试删除Employee实体时:

var employee = dbContext.Employees.AsNoTracking().FirstOrDefault();
dbContext.Entry(employee).State = EntityState.Deleted;
dbContext.SaveChanges();

我遇到以下异常:

  

“ Employee”类型的实体与“ Employee.Address#Address”类型的实体共享表“ Employees”,但是没有具有相同键值“ {Id:1ad382d7-4064- 49a3-87ee-633578175247}”已被标记为“已删除”。

我尝试了指定的Here解决方法,但没有成功。

我正在使用EntityFrameworkCore v2.2.4

2 个答案:

答案 0 :(得分:3)

您面临的问题是由于加载实体的方式引起的。当您这样做时:

var employee = dbContext.Employees.AsNoTracking().FirstOrDefault();

您基本上是对EF说:为我加载此Employee,但请不要理会。将其视为您从未加载过的东西。

接下来,您要删除它。您知道DbContext不会跟踪它,所以您可以这样做:

dbContext.Entry(employee).State = EntityState.Deleted;

这是问题的“关键”。此行告诉DbContext:嗨,请开始跟踪此实体并将其标记为已删除。

问题Employee实体拥有地址,但是DbContext 不知道想要删除它。

您收到的错误消息对实际错误有很多了解,但是乍一看可能并不那么清楚:

  

“ Employee”类型的实体与“ Employee.Address#Address”类型的实体共享表“ Employees”,但是没有具有相同键值“ {Id:1ad382d7-4064- 49a3-87ee-633578175247}”已被标记为“已删除”。

这是说:ID为4的实体Employee被标记为Deleted,但它也具有类型为Address实体未添加要删除。尽管您没有在上下文中将Address声明为DbSet,但就EF而言,它仍然是一个实际的实体。

要修复该问题,同时保持与您相同的代码,还需要添加要删除的Address

context.Entry(employee.Address).State = EntityState.Deleted;

但是:

我不确定这只是示例还是您的真实代码。但是,我个人(并且我也看到很多反对)试图避免手动操纵实体状态。由于您已经体验到了不那么明显的结果,因此这很快就会令人讨厌并产生效果。相反,如果您有一个DbContext来加载要删除的实体,则可以通过将代码更改为以下方式来避免弄乱状态和问题:

var employee = dbContext.Employees.First();

// .Remove will also mark the related entities to be deleted
// If Employee is not tracked, it will also start tracking it
// So, it's not necessary to do .Attach()
dbContext.Remove(employee);

dbContext.SaveChanges();

这将起作用,并且实体将按预期方式删除。当然,如果您的代码不是这样,而实际上您是在断开连接的场景中使用实体的,那么您需要手动将其设置为如上所示,将其删除。

编辑:

正如@IvanStoev在评论中所指出的那样,Remove方法实际上可以解决您面临的行为。 Remove方法会将实体本身以及相关的实体标记为Deleted,并且如果先前未跟踪,还将开始跟踪它们。来自文档:(重点是我)

  

如果已经在“添加”状态下跟踪了该实体,则上下文将停止跟踪该实体(而不是将其标记为“已删除”),因为该实体先前已添加到上下文中并且在数据库中不存在。

     

所有其他尚未被跟踪的可达实体都将以与调用此方法之前调用Attach(Object)相同的方式进行跟踪。 这允许在调用SaveChanges()时应用所有级联操作

DbContext.Remove Method

答案 1 :(得分:0)

您必须使用级联删除,如下所示:

protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
    modelBuilder.Entity<Employee>()
        .HasOptional<Standard>(s => s.Address)
        .WithMany()
        .WillCascadeOnDelete(false);
}
相关问题