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

C# 无法删除在EntityFramework Core中拥有实体的实体,c#,entity-framework-core,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; } } 使用f

我拥有以下实体:

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; }
}
使用fluent 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”类型的实体正在与“Employee.Address#Address”类型的实体共享表“Employees”,但没有具有相同键值“{Id:1ad382d7-4064-49a3-87ee-633578175247}”且已标记为“Deleted”的此类实体

我已尝试了指定的解决方法,但无效


我正在使用EntityFrameworkCore v2.2.4

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

模型创建时受保护的覆盖无效(DbModelBuilder modelBuilder)
{
modelBuilder.Entity()
.has可选(s=>s.Address)
.有很多
.WillCascadeOnDelete(假);
}

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

var employee = dbContext.Employees.AsNoTracking().FirstOrDefault();
你基本上是在对EF说:为我加载这个
员工
,但忘了它吧。将其视为一开始从未加载过

接下来,您要删除它。您知道它没有被
DbContext
跟踪,所以您可以:

dbContext.Entry(employee).State = EntityState.Deleted;
这是问题的“关键”。这行告诉
DbContext
:嘿,请开始跟踪此实体并将其标记为要删除

问题:员工
实体拥有一个地址,但
DbContext
不知道您也想删除它

您收到的错误消息提供了许多关于实际错误的见解,但乍一看可能不那么清楚:

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

这就是说:id为4的实体
Employee
被标记为
Deleted
,但它也有一个类型为
Address
实体,该实体未被添加以删除。尽管在您的上下文中没有将
地址
声明为
数据库集
,但就EF而言,它仍然是一个实际的实体

要在保持代码不变的同时修复它,还需要添加要删除的
地址

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()时应用任何级联操作


您可以在导航上指定
删除
行为,以允许
在级联上删除
。我使用的是OwnsOne,在我的设计中它是一个值对象在我的实际情况中,我有一个大的图形,所以我必须获取它,因为没有跟踪,所以我可以遍历它,并将其与新的图形进行比较,以更新整个图形的导航属性,但我有一个简单易懂的问题。。这可能是另一个问题的主题,但如果您愿意,手动添加要删除的
OwnedEntity
,应该可以将其删除。您确定没有任何其他东西阻止其被删除吗?我刚刚创建了一个示例应用程序,我100%确信它能工作。如果你想试试的话,我甚至可以把它推到GitHub上。如果没有,那是另外一回事。但是,如果你看到错误消息,它是非常清楚的。您与
Address
实体共享
Employee
表,但您告诉
DbContext
您只想删除
Employee
表的一部分。因此,它不知道如何处理表的“其余”部分,即
地址
部分。@SimpleCode您就这样做了:。按照那里关于如何运行它的说明进行操作。
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();