我有以下实体:
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
答案 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()时应用所有级联操作。
答案 1 :(得分:0)
您必须使用级联删除,如下所示:
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<Employee>()
.HasOptional<Standard>(s => s.Address)
.WithMany()
.WillCascadeOnDelete(false);
}