C# 循环依赖于删除(EF核心)
我的数据库中有两个类相互引用,如下面的示例所示C# 循环依赖于删除(EF核心),c#,.net-core,entity-framework-core,C#,.net Core,Entity Framework Core,我的数据库中有两个类相互引用,如下面的示例所示 Parent可以有任意数量的Child对象,我设置了一个外键约束,使其具有Child.ParentID引用Parent.ID;为关系设置DeleteBehavior.Cascade可确保在删除父对象时,也会删除所有子对象 问题是,我还需要引用父类中的子对象之一,在下面的示例中称为PreferredChild。如果我设置了DeleteBehavior.SetNull,我希望在Parent.PreferredChildId和Child.ID之间创建一个
Parent
可以有任意数量的Child
对象,我设置了一个外键约束,使其具有Child.ParentID
引用Parent.ID
;为关系设置DeleteBehavior.Cascade
可确保在删除父对象时,也会删除所有子对象
问题是,我还需要引用父类中的子对象之一,在下面的示例中称为PreferredChild
。如果我设置了DeleteBehavior.SetNull
,我希望在Parent.PreferredChildId
和Child.ID
之间创建一个约束会起作用,但实际发生的情况是,当我删除Parent
对象时,如果设置了PreferredChildId
,我会得到以下异常:
System.InvalidOperationException:'无法保存更改,因为在要保存的数据中检测到循环依赖关系:'Parent[Deleted]PreferredChild PreferredParent{'PreferredChildID'}p.PreferredChild)
.HasForeignKey(p=>p.PreferredChildID)
.OnDelete(DeleteBehavior.SetNull);
建模者
.实体()
.HasOne(c=>c.Parent)
.有许多(p=>p.儿童)
.HasForeignKey(c=>c.ParentID)
.OnDelete(DeleteBehavior.Cascade);
}
}
班级计划{
静态void Main(字符串[]参数){
使用(var context=newtestcontext()){
var parent=新父级();
context.Parents.Add(parent);
SaveChanges();
var child1=new Child{ParentID=parent.ID};
var child2=new Child{ParentID=parent.ID};
context.Children.AddRange(child1,child2);
SaveChanges();
parent.PreferredChildID=child2.ID;
SaveChanges();
//这爆炸了
context.parent.Remove(parent);
SaveChanges();
}
}
}
为什么不做以下事情
公共类父类
{
公共int?ID{get;set;}
public int?LastChildID=>LastChildID?.ID;
公共虚拟子级LastChild=>子级?.LastOrDefault();
公共虚拟ICollection子项{get;set;}
}
编辑:OP编辑完他的问题后,他想要什么就更清楚了
我建议增加第三张这样的桌子
创建表[dbo]。[PreferredChilds](
[ParentId]INT不为NULL,
[PreferredChild]INT不为NULL,
主键群集([ParentId]ASC、[PreferredChild]ASC),
外键([ParentId])引用[dbo]。[Parents]([Id]),
外键([PreferredChild])引用[dbo]。[Children]([Id])
);
这样,循环依赖项就不会有任何问题。
如果删除父项,则preferredchilds
和children
表中的条目也将被删除。那么您正在执行所有这些操作,以便在删除最后一个子项时,父项也会被删除?或者为什么最后一个孩子在那里?为什么不干脆LastChild=>Children.LastOrDefault()代码>否,我想要的是,当删除父项时,子项将被删除。LastChild
在本例中与我在真实代码中的不同,可以在以后更改,因此LastOrDefault
技巧将不起作用。也许这个命名有点误导,我可以更新这个例子。你想在父项中添加最后一个子Id吗?就这样?为什么不在子对象中使用时间戳呢?编辑:我明白了。。。PreferredChild更有意义:)。。。不,那不行。。。从数据库角度看,这总是会导致问题。。。我建议添加第三个表,该表与首选的child和parents具有1:1的连接顺便说一句。。。很可怕,你的键是可空的。我在你的评论后提出了一个问题,将“last”改为“preferred”,以明确它不是最后添加的。此外,我需要一个列,在父表中具有首选子项的id。
using Microsoft.EntityFrameworkCore;
using System.Collections.Generic;
public class Parent {
public int? ID { get; set; }
public int? PreferredChildID { get; set; }
public virtual Child PreferredChild { get; set; }
public virtual ICollection<Child> Children { get; set; }
}
public class Child {
public int? ID { get; set; }
public int? ParentID { get; set; }
public virtual Parent Parent { get; set; }
public virtual Parent PreferringParent { get; set; }
}
public class TestContext : DbContext {
public DbSet<Parent> Parents { get; set; }
public DbSet<Child> Children { get; set; }
protected override void OnConfiguring(DbContextOptionsBuilder options) {
options.UseSqlite("Data Source=test.db");
}
protected override void OnModelCreating(ModelBuilder modelBuilder) {
// I would like to configure the model so that when a Parent is deleted,
// all Children are deleted. Using SetNull on PreferredChildID to avoid object
// being deleted twice
modelBuilder
.Entity<Child>()
.HasOne(c => c.PreferringParent)
.WithOne(p => p.PreferredChild)
.HasForeignKey<Parent>(p => p.PreferredChildID)
.OnDelete(DeleteBehavior.SetNull);
modelBuilder
.Entity<Child>()
.HasOne(c => c.Parent)
.WithMany(p => p.Children)
.HasForeignKey(c => c.ParentID)
.OnDelete(DeleteBehavior.Cascade);
}
}
class Program {
static void Main(string[] args) {
using (var context = new TestContext()) {
var parent = new Parent();
context.Parents.Add(parent);
context.SaveChanges();
var child1 = new Child { ParentID = parent.ID };
var child2 = new Child { ParentID = parent.ID };
context.Children.AddRange(child1, child2);
context.SaveChanges();
parent.PreferredChildID = child2.ID;
context.SaveChanges();
// This explodes
context.Parents.Remove(parent);
context.SaveChanges();
}
}
}