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();
        }

    }
}