[英]Entity Framework Cascade delete - FOREIGN KEY constraint
I have a problem with the following model: 我有以下型号的问题:
public class ProjectPage
{
[Key]
public Guid Id { get; set; }
public Guid? HeaderId { get; set; }
public ProjectPage Header { get; set; }
public Guid? FooterId { get; set; }
public ProjectPage Footer { get; set; }
}
On model Creating I have this: 在模型创建我有这个:
modelBuilder.Entity<ProjectPage>().HasOptional(p => p.Header).WithMany().HasForeignKey(p => p.HeaderId).WillCascadeOnDelete(true);
modelBuilder.Entity<ProjectPage>().HasOptional(p => p.Footer).WithMany().HasForeignKey(p => p.FooterId).WillCascadeOnDelete(true);
But I can't update database. 但我无法更新数据库。 I've got the following error in the Package Manager console:
我在Package Manager控制台中遇到以下错误:
Introducing FOREIGN KEY constraint 'FK_dbo.ProjectPages_dbo.ProjectPages_FooterId' on table 'ProjectPages' may cause cycles or multiple cascade paths.
在表'ProjectPages'上引入FOREIGN KEY约束'FK_dbo.ProjectPages_dbo.ProjectPages_FooterId'可能会导致循环或多个级联路径。 Specify ON DELETE NO ACTION or ON UPDATE NO ACTION, or modify other FOREIGN KEY constraints.
指定ON DELETE NO ACTION或ON UPDATE NO ACTION,或修改其他FOREIGN KEY约束。
Can somebody explain how to remove the Project Page (which can be Footer or Header in another Project Page)? 有人可以解释如何删除项目页面(可以是另一个项目页面中的页脚或页眉)?
That exception is caused when you have multiple paths of cascade deletes that could end trying to delete the same row in DB. 如果有多个级联删除路径可能会导致尝试删除DB中的同一行,则会导致该异常。 Imagine if you have
ProjectPage
with the same Header
and Footer
. 想象一下,如果你的
ProjectPage
具有相同的Header
和Footer
。 When you try to delete that ProjectPage
, due to the configuration of your relationships, there will be two paths trying to delete the same row in DB (one for the Header
and another for the Footer
). 当您尝试删除该
ProjectPage
,由于您的关系配置,将有两个路径尝试删除DB中的同一行(一个用于Header
,另一个用于Footer
)。
You can avoid such ambiguous delete paths by either disabling cascading delete in one of two relationship using Fluent API or by defining some of the relationships as optional (with a nullable foreign key, but you can not configure the relationship with cascade delete). 您可以通过使用Fluent API禁用两个关系之一中的级联删除,或者将某些关系定义为可选(使用可为空的外键,但无法使用级联删除配置关系)来避免此类不明确的删除路径。
Is true, you have both FK as optionals but both relationships have been configured with cascade delete, that's why EF is throwing that exception. 是的,你有两个FK作为选项,但两个关系都配置了级联删除,这就是EF抛出异常的原因。 My recommendation is set only one relationship with cascade delete.
我的建议只与级联删除设置了一个关系。 Regarding the other relationship, I'm afraid you you have to do it manually.
关于其他关系,我担心你必须手动完成。 If you, for example, choose
Footer
to be deleted manually, then when you are going to remove a ProjectPage
,you have to set the Footer
property as null
. 例如,如果您选择手动删除
Footer
,那么当您要删除ProjectPage
,您必须将Footer
属性设置为null
。 The problem here is you could have orphans in your DB. 这里的问题是你的DB中可能有孤儿。 To avoid that, you can override the
SaveChanges
on your Context to find and delete orphans: 为避免这种情况,您可以覆盖Context上的
SaveChanges
来查找和删除孤儿:
public override int SaveChanges()
{
ProjectPages
.Local
.Where(r => r.Footer== null && r.FooterId!=default(Guid)).Select(r=>r.FooterId)
.ToList()
.ForEach(id => ProjectPages.Remove(ProjectPages.Find(id)));
return base.SaveChanges();
}
Another way could be setting the FooterId
with the default(Guid)
value. 另一种方法是使用
default(Guid)
值设置FooterId
。 Due to the type of your PK property ( Id
) is Guid
and it is not Identity, you have to set that property before add a ProjectPage
to the DB. 由于您的PK属性(
Id
)的类型是Guid
而且它不是Identity,您必须在将ProjectPage
添加到DB之前设置该属性。 So, setting the FooterId
with default(Guid)
is another way to mark that entity that you want to delete. 因此,将
FooterId
设置为default(Guid)
是另一种标记要删除的实体的方法。 If you choose this variant, your SaveChanges
method could be as I show below: 如果您选择此变体,您的
SaveChanges
方法可能如下所示:
public override int SaveChanges()
{
ProjectPages
.Local
.Where(r => r.Footer!= null && r.FooterId!=default(Guid)).Select(r=>r.Footer)
.ToList()
.ForEach(pp=> ProjectPages.Remove(pp));
return base.SaveChanges();
}
Or: 要么:
public override int SaveChanges()
{
ProjectPages
.Local
.Where(r => r.Footer!= null && r.FooterId!=default(Guid)).Select(r=>r.Footer)
.ToList()
.ForEach(pp=> Entry(pp).State=EntityState.Deleted);
return base.SaveChanges();
}
This way you can avoid call the Find
method. 这样就可以避免调用
Find
方法。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.