在EF Core中添加外键列

Joe*_*Joe 2 entity-framework ef-migrations ef-core-2.0

我有一个现有表Projects,我想向其中添加一个UserId列,其中UserId有一个外键。 Projects现在有一个名称列表,但我希望每个用户都可以管理自己的项目。我最初拥有“孤儿”是可以的,因为列表足够小,我可以手动清理它们。

我已经更新了模型以包含UserId和导航属性User(可能不相关,但是Entity这里是带有Id和的基类DateModified

public class Project : Entity
{
    public string Name { get; set; }
    public Guid? UserId { get; set; } //tried this as nullable and non nullable
    public User User { get; set; }
}
Run Code Online (Sandbox Code Playgroud)

我相关的映射文件是

public class ProjectMap : IEntityTypeConfiguration<Project>
{
    public void Configure(EntityTypeBuilder<Project> builder)
    {
        builder.Property(x => x.Name).IsRequired();
        builder.Property(x => x.UserId).IsRequired();

        builder.HasOne(x => x.User)
            .WithMany(x => x.Projects)
            .HasForeignKey(x => x.UserId)
            .OnDelete(DeleteBehavior.SetNull);  //I have tried numerous combinations of things here....
    }
}
Run Code Online (Sandbox Code Playgroud)

我还向User项目的实体添加了导航属性,但未对映射类进行任何更改。

public class User : Entity
{
    public string EmailAddress { get; set; }
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public List<Task> Tasks { get; set; }
    public List<Project> Projects { get; set; }
}
Run Code Online (Sandbox Code Playgroud)

由此产生的迁移:

    protected override void Up(MigrationBuilder migrationBuilder)
    {
        migrationBuilder.AddColumn<Guid>(
            name: "UserId",
            table: "Projects",
            nullable: false,
            defaultValue: new Guid("00000000-0000-0000-0000-000000000000"));

        migrationBuilder.CreateIndex(
            name: "IX_Projects_UserId",
            table: "Projects",
            column: "UserId");

        migrationBuilder.AddForeignKey(
            name: "FK_Projects_Users_UserId",
            table: "Projects",
            column: "UserId",
            principalTable: "Users",
            principalColumn: "Id",
            onDelete: ReferentialAction.SetNull);
    }
Run Code Online (Sandbox Code Playgroud)

运行更新数据库时会转换为此SQL

ALTER TABLE [Projects] ADD CONSTRAINT [FK_Projects_Users_UserId] FOREIGN KEY ([UserId]) REFERENCES [Users] ([Id]) ON DELETE SET NULL;
Run Code Online (Sandbox Code Playgroud)

并因此错误而失败

Cannot create the foreign key "FK_Projects_Users_UserId" with the SET NULL referential action, because one or more referencing columns are not nullable.

我究竟做错了什么?

jpg*_*ssi 7

下面的行导致了此问题,因为您希望在FK上设置一个可为空的FK Project

builder.Property(x => x.UserId).IsRequired();
Run Code Online (Sandbox Code Playgroud)

另外,您生成的迁移已经具有提示:

 nullable: false,
Run Code Online (Sandbox Code Playgroud)

只需在您的Configure方法上删除该行即可。您可能还需要通过运行Remove-Migration然后再次运行add-migration 来删除迁移。这次您应该具有以下功能:

protected override void Up(MigrationBuilder migrationBuilder)
{
    migrationBuilder.AlterColumn<string>(
        name: "Name",
        table: "Projects",
        nullable: false,
        oldClrType: typeof(string),
        oldNullable: true);

    migrationBuilder.AddColumn<Guid>(
        name: "UserId",
        table: "Projects",
        nullable: true);

    migrationBuilder.CreateIndex(
        name: "IX_Projects_UserId",
        table: "Projects",
        column: "UserId");

    migrationBuilder.AddForeignKey(
        name: "FK_Projects_Users_UserId",
        table: "Projects",
        column: "UserId",
        principalTable: "Users",
        principalColumn: "Id",
        onDelete: ReferentialAction.SetNull);
}
Run Code Online (Sandbox Code Playgroud)

如果需要,可以通过IsRequired在配置关系时显式设置来更具体:

private static void ConfigureProject(EntityTypeBuilder<Project> b)
{
    b.Property(x => x.Name).IsRequired();

    b.HasOne(x => x.User)
        .WithMany(x => x.Projects)
        .HasForeignKey(x => x.UserId)
        .IsRequired(false)
        .OnDelete(DeleteBehavior.SetNull);
}
Run Code Online (Sandbox Code Playgroud)

即使该属性已经可以为空,并且EF会使用其约定正确地创建它,我还是喜欢它,对于不需要阅读实际Project类的人来说,阅读配置并了解它仍然很不错。