多个外键 EF Core 2.1 代码首先在 DB 中只生成 1 个

Multiple Foreign Keys EF Core 2.1 code first generates only 1 in DB

我有以下型号:

public class User : AuditedModel
{
    public string Login { get; }
    public string PasswordHash { get; }

    public User(string login, string password, User creationUser) : base(creationUser)
    {
        Login = login;
        PasswordHash = GeneratePasswordHash(password);
    }

    protected User(int id, DateTime creationDate, DateTime? terminationDate, string login, string passwordHash) : base(id, 0, creationDate)
    {
        Login = login;
        PasswordHash = passwordHash;
    }
}
public class AuditedModel : BaseModel
{
    protected AuditedModel(User creationUser)
    {
        CreationUser = creationUser;
        CreationDate = DateTime.UtcNow;
    }

    public User CreationUser { get; protected set; }
    public DateTime CreationDate { get; }
    public User TerminationUser { get; protected set; }
    public DateTime? TerminationDate { get; }
}
public class BaseModel
{
    public int Id { get;}

    protected BaseModel()
    {
    }

    public BaseModel(int id)
    {
        Id = id;
    }
}

EF Core Code First 应该为 CreationUserTerminationUser 创建两个自引用外键,如 Fluent API:

中所指定
public class UserConfiguration : IEntityTypeConfiguration<User>
{
    public void Configure(EntityTypeBuilder<User> builder)
    {
        builder.ToTable(nameof(User));

        builder.HasKey(u => u.Id);
        builder.Property(u => u.Id).ValueGeneratedOnAdd();

        builder.Property(u => u.Login)
            .IsRequired();

        builder.Property(u => u.PasswordHash);

        builder.Property(u => u.CreationDate);

        builder.Property(u => u.TerminationDate);

        builder.HasOne(u => u.TerminationUser)
            .WithOne()
            .HasForeignKey<User>(u => u.Id)
            .OnDelete(DeleteBehavior.Restrict);

        builder.HasOne(u => u.CreationUser)
            .WithOne()
            .IsRequired()
            .HasForeignKey<User>(u => u.Id)
            .OnDelete(DeleteBehavior.Restrict);
     }
}

我打算为这个项目使用迁移,所以我将展示迁移输出,它反映在数据库中:

public partial class InitialCreate : Migration
{
    protected override void Up(MigrationBuilder migrationBuilder)
    {
        migrationBuilder.EnsureSchema(
            name: "public");

        migrationBuilder.CreateTable(
            name: "User",
            schema: "public",
            columns: table => new
            {
                Id = table.Column<int>(nullable: false)
                    .Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.SerialColumn),
                CreationDate = table.Column<DateTime>(nullable: false),
                TerminationUserId = table.Column<int>(nullable: true),
                TerminationDate = table.Column<DateTime>(nullable: true),
                Login = table.Column<string>(nullable: false),
                PasswordHash = table.Column<string>(nullable: true)
            },
            constraints: table =>
            {
                table.PrimaryKey("PK_User", x => x.Id);
                table.ForeignKey(
                    name: "FK_User_User_TerminationUserId",
                    column: x => x.TerminationUserId,
                    principalSchema: "public",
                    principalTable: "User",
                    principalColumn: "Id",
                    onDelete: ReferentialAction.Restrict);
            });

        migrationBuilder.CreateIndex(
            name: "IX_User_TerminationUserId",
            schema: "public",
            table: "User",
            column: "TerminationUserId");
    }

    protected override void Down(MigrationBuilder migrationBuilder)
    {
        migrationBuilder.DropTable(
            name: "User",
            schema: "public");
    }
}

如图所示,即使 CreationUser 导航设置为必需,也只有 TerminationUserId ForeignKey 列。

    I'm using:
    - Npgsql.EntityFrameworkCore.PostgreSQL 2.1.1
    - Microsoft.NETCore.App 2.1.0
    - Microsoft.EntityFrameworkCore.Tools.DotNet 2.0.3
    - Microsoft.EntityFrameworkCore.Design 2.1.1
    - Microsoft.VisualStudio.Web.CodeGeneration.Tools 2.0.4

我很惊讶 TerminationUserId 外键列被创建了,因为它在您的模型中不存在。如果你想创建这两个外键列,那么你需要将它们添加到你的模型中,如下所示:

public class AuditedModel : BaseModel
{
    protected AuditedModel(User creationUser)
    {
        CreationUser = creationUser;
        CreationDate = DateTime.UtcNow;
    }

    public int CreationUserId { get; protected set; }
    public User CreationUser { get; protected set; }
    public DateTime CreationDate { get; }
    public int? TerminationUserId { get; protected set; }
    public User TerminationUser { get; protected set; }
    public DateTime? TerminationDate { get; }
}

然后在您的 Fluent API 映射中:

    builder.HasOne(u => u.TerminationUser)
        .WithOne()
        .HasForeignKey<User>(u => u.TerminationUserId)
        .OnDelete(DeleteBehavior.Restrict);

    builder.HasOne(u => u.CreationUser)
        .WithOne()
        .IsRequired()
        .HasForeignKey<User>(u => u.CreationUserId)
        .OnDelete(DeleteBehavior.Restrict);