为什么我的必需属性未在Entity Framework Core中被级联删除?

时间:2017-03-28 17:28:31

标签: c# postgresql entity-framework-core npgsql

我有以下具有必需属性的类,并且遇到通过级联删除删除依赖属性的问题。

public class City
{
     [Key]
     public int Id {get; private set; }

     [Required]
     public ZipCode ZipCode { get; private set; }
}


public class ZipCode
{
    [Key]
    public int Id { get; private set; }

    [Required]
    public string Zip { get; private set; }
}

如果我尝试使用以下代码加载城市以将其删除:

City toDelete = db.CitySet.Where(c => c.Id == myCityReference.Id).FirstOrDefault();
db.CitySet.Remove(toDelete);
db.SaveChanges();

我得到一个Microsoft.EntityFrameworkCore.DbUpdateException,其内部例外是Npgsql.PostgresException: 23503: Update or delete on table "..." violates foreign key constraint.

但是,如果我在上面添加一个include语句,那就是:

City toDelete = db.CitySet.Include(c => c.ZipCode)
    .Where(c => c.Id == myCityReference.Id).FirstOrDefault();

然后删除成功删除了城市和邮政编码。但是,根据EFCore docs,必需关系将设置为Cascade删除功能。为什么这不会发生?我正在使用Entity Framework Core v1.1.1和Npgsql v3.2.2。

1 个答案:

答案 0 :(得分:5)

以下是从您的模型生成的迁移:

migrationBuilder.CreateTable(
    name: "ZipCodes",
    columns: table => new
    {
        Id = table.Column<int>(nullable: false)
            .Annotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn),
        Zip = table.Column<string>(nullable: false)
    },
    constraints: table =>
    {
        table.PrimaryKey("PK_ZipCodes", x => x.Id);
    });

migrationBuilder.CreateTable(
    name: "Cities",
    columns: table => new
    {
        Id = table.Column<int>(nullable: false)
            .Annotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn),
        ZipCodeId = table.Column<int>(nullable: false)
    },
    constraints: table =>
    {
        table.PrimaryKey("PK_Cities", x => x.Id);
        table.ForeignKey(
            name: "FK_Cities_ZipCodes_ZipCodeId",
            column: x => x.ZipCodeId,
            principalTable: "ZipCodes",
            principalColumn: "Id",
            onDelete: ReferentialAction.Cascade);
    });

migrationBuilder.CreateIndex(
    name: "IX_Cities_ZipCodeId",
    table: "Cities",
    column: "ZipCodeId");

(它适用于Sql Server,但除了一些特定的标识列注释外,对于其他数据库类型应该是相同的)

如您所见,FK是在启用级联删除的情况下创建的。但是,FK列位于City表中,并引用ZipCode表。这意味着ZipCode是主体,City是依赖。因此,删除ZipCode将级联删除City,而不是相反。

ZipCodeId列上没有唯一的约束/索引,这表明EF Core假定one-to-many关系(一个ZipCode到多个City)。

如果您希望它以相反的方向级联,那么您需要使用以下流畅的配置将FK列放在ZipCode表中:

modelBuilder.Entity<City>()
    .HasOne(e => e.ZipCode)
    .WithOne()
    .HasForeignKey<ZipCode>("CityId")
    .OnDelete(DeleteBehavior.Cascade);

现在迁移看起来像这样:

migrationBuilder.CreateTable(
    name: "Cities",
    columns: table => new
    {
        Id = table.Column<int>(nullable: false)
            .Annotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn)
    },
    constraints: table =>
    {
        table.PrimaryKey("PK_Cities", x => x.Id);
    });

migrationBuilder.CreateTable(
    name: "ZipCodes",
    columns: table => new
    {
        Id = table.Column<int>(nullable: false)
            .Annotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn),
        CityId = table.Column<int>(nullable: true),
        Zip = table.Column<string>(nullable: false)
    },
    constraints: table =>
    {
        table.PrimaryKey("PK_ZipCodes", x => x.Id);
        table.ForeignKey(
            name: "FK_ZipCodes_Cities_CityId",
            column: x => x.CityId,
            principalTable: "Cities",
            principalColumn: "Id",
            onDelete: ReferentialAction.Cascade);
    });

migrationBuilder.CreateIndex(
    name: "IX_ZipCodes_CityId",
    table: "ZipCodes",
    column: "CityId",
    unique: true);

即。级联是从CityZipCode,关系是one-to-one(请注意唯一索引)。

现在,我不确定这里的真正意图是什么。但是确保数据库级联删除是有方向性的 - 从引用表到引用表。