EF Core 2.1:具有一对多关系的自引用实体生成附加列

Posted

技术标签:

【中文标题】EF Core 2.1:具有一对多关系的自引用实体生成附加列【英文标题】:EF Core 2.1: Self-referencing entity with one to many relationship generates additional column 【发布时间】:2018-07-12 14:28:24 【问题描述】:

我有以下实体:

public class Level

    public int LevelId  get; set; 
    public int? ParentLevelId  get; set; 
    public string Name  get; set; 

    public virtual Level Parent  get; set; 
    public virtual HashSet<Level> Children  get; set;    

我在这里遇到的问题是 Children 属性,它在 Fluent API 中是这样配置的:

modelBuilder.Entity<Level>()
    .HasOne(x => x.Parent)
    .WithMany(x => x.Children)
    .HasForeignKey(x => x.ParentLevelId);

这会导致迁移添加了一些额外的列:

migrationBuilder.AddColumn<int>(
    name: "LevelId1",
    table: "Level",
    nullable: true);

migrationBuilder.CreateIndex(
    name: "IX_Level_LevelId1",
    table: "Level",
    column: "LevelId1");

migrationBuilder.AddForeignKey(
    name: "FK_Level_Level_LevelId1",
    table: "Level",
    column: "LevelId1",
    principalTable: "Level",
    principalColumn: "LevelId",
    onDelete: ReferentialAction.Restrict);

我在这里做错了什么?

编辑:问题被标记为可能与this question 重复;但是,在这种情况下,模型生成工作 - 问题是加载数据。而这里的问题是生成了一个额外的列。

【问题讨论】:

LevelId 是 PK 吗?如果是这样,请尝试在属性上方添加关键注释,例如:[Key] public int LevelId get;set; 这是所有代码还是为了简洁而省略了其他属性? @dickrichie 刚刚尝试添加 [Key] 属性,同样的事情发生了。 @GertArnold 为简洁起见,我省略了其他内容。我还有一些字符串和 DateTime 属性,没什么复杂的。 Map category parent id self referencing table structure to EF Core entity的可能重复 【参考方案1】:

您的迁移出现问题。初始化该模型时没有重现:

using Microsoft.EntityFrameworkCore;
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Linq;

namespace EfCoreTest


    public class Level
    
        public int LevelId  get; set; 
        public int? ParentLevelId  get; set; 
        public string Name  get; set; 

        public virtual Level Parent  get; set; 
        public virtual HashSet<Level> Children  get; set; 
    

    public class Db : DbContext
    
        public DbSet<Level> levels  get; set; 


        protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
        
            optionsBuilder.UseSqlServer("server=.;database=EfCoreTest;Integrated Security=true");
            base.OnConfiguring(optionsBuilder);
        

        protected override void OnModelCreating(ModelBuilder modelBuilder)
        
            base.OnModelCreating(modelBuilder);
            modelBuilder.Entity<Level>()
                        .HasOne(x => x.Parent)
                        .WithMany(x => x.Children)
                        .HasForeignKey(x => x.ParentLevelId);

        
    

    class Program
    
        static void Main(string[] args)
        
            using (var db = new Db())
            
                db.Database.EnsureDeleted();
                db.Database.EnsureCreated();

                Console.ReadKey();



            

        
    

创建表:

CREATE TABLE [levels] (
    [LevelId] int NOT NULL IDENTITY,
    [ParentLevelId] int NULL,
    [Name] nvarchar(max) NULL,
    CONSTRAINT [PK_levels] PRIMARY KEY ([LevelId]),
    CONSTRAINT [FK_levels_levels_ParentLevelId] FOREIGN KEY ([ParentLevelId]) REFERENCES [levels] ([LevelId]) ON DELETE NO ACTION
);

添加了迁移,

PM> Add-Migration InitialCreate

仍然没有复制:

using Microsoft.EntityFrameworkCore.Metadata;
using Microsoft.EntityFrameworkCore.Migrations;

namespace EfCoreTest.Migrations

    public partial class InitialCreate : Migration
    
        protected override void Up(MigrationBuilder migrationBuilder)
        
            migrationBuilder.CreateTable(
                name: "levels",
                columns: table => new
                
                    LevelId = table.Column<int>(nullable: false)
                        .Annotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn),
                    ParentLevelId = table.Column<int>(nullable: true),
                    Name = table.Column<string>(nullable: true)
                ,
                constraints: table =>
                
                    table.PrimaryKey("PK_levels", x => x.LevelId);
                    table.ForeignKey(
                        name: "FK_levels_levels_ParentLevelId",
                        column: x => x.ParentLevelId,
                        principalTable: "levels",
                        principalColumn: "LevelId",
                        onDelete: ReferentialAction.Restrict);
                );

            migrationBuilder.CreateIndex(
                name: "IX_levels_ParentLevelId",
                table: "levels",
                column: "ParentLevelId");
        

        protected override void Down(MigrationBuilder migrationBuilder)
        
            migrationBuilder.DropTable(
                name: "levels");
        
    

【讨论】:

我刚刚删除了数据库,删除了所有迁移并创建了一个新的初始迁移。同样的问题,它会创建一个 LevelId 属性(键)、ParentLevelId 和那个额外的 LevelId1。 尝试明确并用[ForeignKey("ParentLevelId")] 装饰您的Parent 属性,以明确告诉实体您想将该列用作该导航属性的FK。 @DavideDeSantis 添加了迁移。仍然没有复制。 我刚刚在周末后重试,突然就可以了。我没有改变任何东西,做了和上周一样的事情(例如删除数据库、删除所有迁移、重新创建初始迁移)。上周没有多次工作的东西现在完美无缺,不知道为什么。无论如何感谢您的帮助! 在新问题中发布重现。

以上是关于EF Core 2.1:具有一对多关系的自引用实体生成附加列的主要内容,如果未能解决你的问题,请参考以下文章

具有一对一关系的 EF Core 批量插入

定义引用同一个表的多对多关系(EF7/core)

EF Core中通过Fluent API配置一对多关系

EF Core中通过Fluent API配置一对多关系

EF Core中通过Fluent API配置一对多关系

EF Core中通过Fluent API配置一对多关系