简体   繁体   English

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

[英]How to define a EF 6 Parent Child Relation with variable?

I try to setup Entity Framework to define a parent child relation with variable for the relation.我尝试设置实体框架来定义一个带有变量的父子关系。 An Item is composed by a ratio of other Items.一个 Item 由其他 Item 的比例组成。 How can I design this with Entity Framework 6.如何使用 Entity Framework 6 设计它。

Lets say my Parent item id a pound cake and my Child are Egg, Flour, Sugar and Butter.假设我的父项是一磅蛋糕,我的孩子是鸡蛋、面粉、糖和黄油。 For pound cake the ratio of each Child is 1. Let's keep this simple.对于磅蛋糕,每个孩子的比例是 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.
    }

My initial DbContext is我最初的 DbContext 是

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

On migration creation with Add-Migration Initial .关于使用Add-Migration Initial迁移创建。 The system generate this migration code:系统生成此迁移代码:

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

        }

The migration created my 2 entities but they have no relation.迁移创建了我的 2 个实体,但它们没有关系。 So I cannot navigate easily from my item table to all my compositions.所以我无法轻松地从我的项目表导航到我的所有作品。 I use Linpad and LinqPad do not create the navigation property.我使用 Linpad 和 LinqPad 不创建导航属性。 This is normal, I never say to my POCO taht ParentId and ChildId must be Id existing into my Item table.这是正常的,我从来没有对我的 POCO 说 ParentId 和 ChildId 必须是 Id 存在于我的 Item 表中。

To create the constraint in my database and be able to navigate I think I must add the navigation property and link it to foreign key.要在我的数据库中创建约束并能够导航,我想我必须添加导航属性并将其链接到外键。 I will try several solution here and comment.我将在这里尝试几种解决方案并发表评论。

Solution 1 - Just add navigation properties解决方案 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.
    }

On migration creation with Add-Migration Initial.关于使用 Add-Migration Initial 的迁移创建。 The system generate this migration code:系统生成此迁移代码:

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

        }

But why the cascadeDelete to true?但是为什么cascadeDelete 为true? I don't want to delete all my tree on each item deletion.我不想在每个项目删除时删除我的所有树。 I know I can define Child or ChildId nullable and Parent or ParentId nullable but I cannot accept that.我知道我可以定义 Child 或 ChildId 可以为空,而 Parent 或 ParentId 可以为空,但我不能接受。 In my Composition table Parent and Child cannot be null.在我的 Composition 表中,Parent 和 Child 不能为空。 This is not logic I create a link with a value on one side and null on the other side.这不是逻辑,我创建了一个链接,一边是一个值,另一边是 null。 I can delete the composition link but if I create it then both side must exist.我可以删除合成链接,但如果我创建它,那么双方都必须存在。

Also, I cannot Udpate-Database this version because另外,我不能Udpate-Database这个版本,因为

Introducing FOREIGN KEY constraint 'FK_dbo.Compositions_dbo.Items_ParentId' on table 'Compositions' may cause cycles or multiple cascade paths.在表 'Compositions' 上引入 FOREIGN KEY 约束 'FK_dbo.Compositions_dbo.Items_ParentId' 可能会导致循环或多个级联路径。 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 约束。

Solution 2 - Using Fluent API解决方案 2 - 使用 Fluent API

I take my solution 1 and I try to add missing information to help the system in my OnModelCreating() override我采用我的解决方案 1,并尝试在我的 OnModelCreating() 覆盖中添加缺失的信息以帮助系统

    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<Composition>()
                .HasRequired(c => c.Parent)
                // And I have no idea how to do here
        }
    }

Solution 3 - Composition collection on Item解决方案 3 - 项目上的组合收集

    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.
    }

On migration creation with Add-Migration Initial.关于使用 Add-Migration Initial 的迁移创建。 The system generate this migration code:系统生成此迁移代码:

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

}

Seems better but still I have a cascade delete to true.看起来更好,但我仍然有一个级联删除为真。

Solution 4 - bring parent and child compositions on Item解决方案 4 - 在 Item 上引入父子组合

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.
}

The migration script is then然后迁移脚本是

        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),
                        Item_Id = c.Guid(),
                        Item_Id1 = c.Guid(),
                    })
                .PrimaryKey(t => t.Id)
                .ForeignKey("dbo.Items", t => t.Item_Id)
                .ForeignKey("dbo.Items", t => t.Item_Id1)
                .ForeignKey("dbo.Items", t => t.ChildId, cascadeDelete: true)
                .ForeignKey("dbo.Items", t => t.ParentId, cascadeDelete: true)
                .Index(t => t.ParentId)
                .Index(t => t.ChildId)
                .Index(t => t.Item_Id)
                .Index(t => t.Item_Id1);

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

        }

Logic.逻辑。 The systen cannot know my named ParentComposition collection must be linked to ParentId foreign key and the ChildComposition collection must be linked to the ChildId foreign key?系统不知道我命名的 ParentComposition 集合必须链接到 ParentId 外键,而 ChildComposition 集合必须链接到 ChildId 外键? So the system create new foreign keys.因此系统会创建新的外键。

On Update-Database I get errorUpdate-Database我收到错误

Introducing FOREIGN KEY constraint 'FK_dbo.Compositions_dbo.Items_ParentId' on table 'Compositions' may cause cycles or multiple cascade paths.在表 'Compositions' 上引入 FOREIGN KEY 约束 'FK_dbo.Compositions_dbo.Items_ParentId' 可能会导致循环或多个级联路径。 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 约束。

Solution 5 - Back again to Fluent API解决方案 5 - 再次返回 Fluent API

But I can navigate through properties now so maybe I will find what I'm looking for:但我现在可以浏览属性,所以也许我会找到我要找的东西:

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

Gives me the migration script:给我迁移脚本:

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

        }

And I can update my database with this one.我可以用这个更新我的数据库。 But, wierd, when I test he result in LinqPad it seems there is a inversion between ChildCompositions list and ParentCompositions list.但是,奇怪的是,当我在 LinqPad 中测试他的结果时,似乎 ChildCompositions 列表和 ParentCompositions 列表之间存在反转。

在此处输入图片说明

I tried to change that in my Fluent API without success.我试图在我的 Fluent API 中改变它,但没有成功。

A solution could be to remove the ForeignKey attributes but instead adding the relationship through the model builder:解决方案可能是删除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);
}

Thereby, you can explicitly set the delete cascade behavior.因此,您可以显式设置删除级联行为。

I tried it by adding a Composition entity and two Item s, and then deleting the Composition entity.我通过添加一个Composition实体和两个Item尝试它,然后删除Composition实体。 The two Item s are remaining in the database.这两个Item保留在数据库中。 I think that's the way it is intended to be.我认为这就是它的本意。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM