C# 如何使用变量定义EF 6父子关系?

C# 如何使用变量定义EF 6父子关系?,c#,entity-framework-6,parent-child,ef-fluent-api,C#,Entity Framework 6,Parent Child,Ef Fluent Api,我尝试设置实体框架,用关系的变量定义父子关系。一个项目由其他项目的比率组成。如何使用EntityFramework6进行设计 假设我的父母给我一磅蛋糕,我的孩子给我鸡蛋、面粉、糖和黄油。对于磅蛋糕,每个孩子的比例是1。让我们保持简单 public class Item { public Guid Id { get; set; } public string Name { get; set; } } public class Com

我尝试设置实体框架,用关系的变量定义父子关系。一个项目由其他项目的比率组成。如何使用EntityFramework6进行设计

假设我的父母给我一磅蛋糕,我的孩子给我鸡蛋、面粉、糖和黄油。对于磅蛋糕,每个孩子的比例是1。让我们保持简单

    public class Item
    {
        public Guid Id { get; set; }
        public string Name { get; set; }
    }

    public class Composition
    {
        public Guid Id { get; set; }

        public Guid ParentId { get; set; } // Id of Parent Item

        public Guid ChildId { get; set; } //  Id of Child Item

        public int Ratio { get; set; } // Number of Child that compose the parent.
    }
我最初的DbContext是

    public class TestContext : DbContext
    {
        public DbSet<Item> Items { get; set; }
    }
迁移创建了我的2个实体,但它们没有关系。因此,我无法轻松地从项目表导航到所有作文。我使用Linpad,LinqPad不创建导航属性。这是正常的,我从来不会对我的POCO taht ParentId和ChildId说必须是存在于我的项目表中的Id

要在数据库中创建约束并能够导航,我认为必须添加导航属性并将其链接到外键。我将在这里尝试几种解决方案并发表评论

解决方案1-只需添加导航属性

    public class Composition
    {
        public Guid Id { get; set; }

        public Guid ParentId { get; set; } // Id of Parent Item

        [ForeignKey(nameof(ParentId))]
        public Item Parent { get; set; }

        public Guid ChildId { get; set; } //  Id of Child Item

        [ForeignKey(nameof(ChildId))]
        public Item Child { get; set; }

        public int Ratio { get; set; } // Number of Child that compose the parent.
    }
使用“添加迁移初始值”创建迁移时。系统将生成此迁移代码:

        public override void Up()
        {
            CreateTable(
                "dbo.Compositions",
                c => new
                    {
                        Id = c.Guid(nullable: false),
                        ParentId = c.Guid(nullable: false),
                        ChildId = c.Guid(nullable: false),
                        Ratio = c.Int(nullable: false),
                    })
                .PrimaryKey(t => t.Id);

            CreateTable(
                "dbo.Items",
                c => new
                    {
                        Id = c.Guid(nullable: false),
                        Name = c.String(),
                    })
                .PrimaryKey(t => t.Id);

        }
        public override void Up()
        {
            CreateTable(
                "dbo.Compositions",
                c => new
                    {
                        Id = c.Guid(nullable: false),
                        ParentId = c.Guid(nullable: false),
                        ChildId = c.Guid(nullable: false),
                        Ratio = c.Int(nullable: false),
                    })
                .PrimaryKey(t => t.Id)
                .ForeignKey("dbo.Items", t => t.ChildId, cascadeDelete: true)
                .ForeignKey("dbo.Items", t => t.ParentId, cascadeDelete: true)
                .Index(t => t.ParentId)
                .Index(t => t.ChildId);

            CreateTable(
                "dbo.Items",
                c => new
                    {
                        Id = c.Guid(nullable: false),
                        Name = c.String(),
                    })
                .PrimaryKey(t => t.Id);

        }
public override void Up()
{
    CreateTable(
        "dbo.Compositions",
        c => new
            {
                Id = c.Guid(nullable: false),
                ParentId = c.Guid(nullable: false),
                ChildId = c.Guid(nullable: false),
                Ratio = c.Int(nullable: false),
            })
        .PrimaryKey(t => t.Id)
        .ForeignKey("dbo.Items", t => t.ChildId, cascadeDelete: true)
        .Index(t => t.ChildId);

    CreateTable(
        "dbo.Items",
        c => new
            {
                Id = c.Guid(nullable: false),
                Name = c.String(),
            })
        .PrimaryKey(t => t.Id);

}

但为什么这个结论是正确的呢?我不想每次删除项目时都删除我的所有树。我知道我可以定义Child或ChildId为null,Parent或ParentId为null,但我不能接受这一点。在我的合成表中,父级和子级不能为空。这不是我创建一个一边有值另一边为空的链接的逻辑。我可以删除合成链接,但如果我创建了它,那么双方都必须存在

此外,我无法
Udpate数据库
此版本,因为

在表“Compositions”上引入外键约束“FK_dbo.Compositions_dbo.Items_ParentId”可能会导致循环或多个级联路径。指定“在删除时不执行操作”或“在更新时不执行操作”,或修改其他外键约束

解决方案2-使用Fluent API

我采用我的解决方案1,尝试添加缺少的信息,以帮助系统实现OnModelCreating()覆盖

似乎更好,但我仍然有一个级联删除为真

解决方案4-将家长和孩子的作文带到项目上

    public class Item
    {
        public Guid Id { get; set; }

        public string Name { get; set; }

        public ICollection<Composition> Compositions { get; set; }
    }

    public class Composition
    {
        public Guid Id { get; set; }

        public Guid ParentId { get; set; } // Id of Parent Item

        //[ForeignKey(nameof(ParentId))]
        //public Item Parent { get; set; }

        public Guid ChildId { get; set; } //  Id of Child Item

        [ForeignKey(nameof(ChildId))]
        public Item Child { get; set; }

        public int Ratio { get; set; } // Number of Child that compose the parent.
    }
public class Item
{
    public Guid Id { get; set; }

    public string Name { get; set; }

    public ICollection<Composition> ChildCompositions { get; set; }

    public ICollection<Composition> ParentCompositions { get; set; }
}

public class Composition
{
    public Guid Id { get; set; }

    public Guid ParentId { get; set; } // Id of Parent Item

    [ForeignKey(nameof(ParentId))]
    public Item Parent { get; set; }

    public Guid ChildId { get; set; } //  Id of Child Item

    [ForeignKey(nameof(ChildId))]
    public Item Child { get; set; }

    public int Ratio { get; set; } // Number of Child that compose the parent.
}
逻辑。系统无法知道我的命名ParentComposition集合必须链接到ParentId外键,而ChildComposition集合必须链接到ChildId外键?因此,系统会创建新的外键

updatedatabase
上,我发现了一个错误

在表“Compositions”上引入外键约束“FK_dbo.Compositions_dbo.Items_ParentId”可能会导致循环或多个级联路径。指定“在删除时不执行操作”或“在更新时不执行操作”,或修改其他外键约束

解决方案5-再次返回Fluent API

但我现在可以浏览酒店,所以也许我会找到我要找的:

public class Item
    {
        public Guid Id { get; set; }

        public string Name { get; set; }

        public ICollection<Composition> ParentCompositions { get; set; }

        public ICollection<Composition> ChildCompositions { get; set; }
    }

    public class Composition
    {
        public Guid Id { get; set; }

        public Guid ParentId { get; set; } // Id of Parent Item

        [ForeignKey(nameof(ParentId))]
        public Item Parent { get; set; }

        public Guid ChildId { get; set; } //  Id of Child Item

        [ForeignKey(nameof(ChildId))]
        public Item Child { get; set; }

        public int Ratio { get; set; } // Number of Child that compose the parent.
    }

    public class TestContext : DbContext
    {
        public DbSet<Item> Items { get; set; }
        public DbSet<Composition> Compositions { get; set; }

        protected override void OnModelCreating(DbModelBuilder modelBuilder)
        {
            base.OnModelCreating(modelBuilder);

            modelBuilder.Entity<Item>()
                .HasMany(c => c.ChildCompositions)
                .WithRequired(c => c.Parent)
                //.HasForeignKey(c => c.ParentId)
                .WillCascadeOnDelete(false);

            modelBuilder.Entity<Item>()
                .HasMany(c => c.ParentCompositions)
                .WithRequired(c => c.Child)
                //.HasForeignKey(c => c.ChildId)
                .WillCascadeOnDelete(false);
        }
    }
我可以用这个更新我的数据库。但是,维尔德,当我在LinqPad中测试他的结果时,似乎在ChildCompositions列表和ParentCompositions列表之间有一个反转


我试图在我的Fluent API中改变这一点,但没有成功。

解决方案可能是删除
ForeignKey
属性,而是通过模型生成器添加关系:

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    modelBuilder.Entity<Composition>().HasOne<Item>(x => x.ChildItem).WithMany().OnDelete(DeleteBehavior.Restrict);
    modelBuilder.Entity<Composition>().HasOne<Item>(x => x.ParentItem).WithMany().OnDelete(DeleteBehavior.Restrict);
}
模型创建时受保护的覆盖无效(ModelBuilder ModelBuilder)
{
modelBuilder.Entity().HasOne(x=>x.ChildItem).WithMany().OnDelete(DeleteBehavior.Restrict);
modelBuilder.Entity().HasOne(x=>x.ParentItem).WithMany().OnDelete(DeleteBehavior.Restrict);
}
因此,可以显式设置删除级联行为


我试着添加一个
组合
实体和两个
s,然后删除
组合
实体。两个
仍保留在数据库中。我想这就是它的意图。

您能发布迁移脚本中的一个片段吗?-我想你可以手动适应。删除级联不受数据批注的控制,而仅由fluent API调用
OnDelete()
控制,在该调用中可以设置行为。@lzydrmr我用所有测试重新编写了我的问题
public class Item
    {
        public Guid Id { get; set; }

        public string Name { get; set; }

        public ICollection<Composition> ParentCompositions { get; set; }

        public ICollection<Composition> ChildCompositions { get; set; }
    }

    public class Composition
    {
        public Guid Id { get; set; }

        public Guid ParentId { get; set; } // Id of Parent Item

        [ForeignKey(nameof(ParentId))]
        public Item Parent { get; set; }

        public Guid ChildId { get; set; } //  Id of Child Item

        [ForeignKey(nameof(ChildId))]
        public Item Child { get; set; }

        public int Ratio { get; set; } // Number of Child that compose the parent.
    }

    public class TestContext : DbContext
    {
        public DbSet<Item> Items { get; set; }
        public DbSet<Composition> Compositions { get; set; }

        protected override void OnModelCreating(DbModelBuilder modelBuilder)
        {
            base.OnModelCreating(modelBuilder);

            modelBuilder.Entity<Item>()
                .HasMany(c => c.ChildCompositions)
                .WithRequired(c => c.Parent)
                //.HasForeignKey(c => c.ParentId)
                .WillCascadeOnDelete(false);

            modelBuilder.Entity<Item>()
                .HasMany(c => c.ParentCompositions)
                .WithRequired(c => c.Child)
                //.HasForeignKey(c => c.ChildId)
                .WillCascadeOnDelete(false);
        }
    }
        public override void Up()
        {
            CreateTable(
                "dbo.Compositions",
                c => new
                    {
                        Id = c.Guid(nullable: false),
                        ParentId = c.Guid(nullable: false),
                        ChildId = c.Guid(nullable: false),
                        Ratio = c.Int(nullable: false),
                    })
                .PrimaryKey(t => t.Id)
                .ForeignKey("dbo.Items", t => t.ParentId)
                .ForeignKey("dbo.Items", t => t.ChildId)
                .Index(t => t.ParentId)
                .Index(t => t.ChildId);

            CreateTable(
                "dbo.Items",
                c => new
                    {
                        Id = c.Guid(nullable: false),
                        Name = c.String(),
                    })
                .PrimaryKey(t => t.Id);

        }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    modelBuilder.Entity<Composition>().HasOne<Item>(x => x.ChildItem).WithMany().OnDelete(DeleteBehavior.Restrict);
    modelBuilder.Entity<Composition>().HasOne<Item>(x => x.ParentItem).WithMany().OnDelete(DeleteBehavior.Restrict);
}