DeleteBehavior.Restrict 的 EF Core 级联引用完整性无法正常工作

EF Core Cascading Referential Integrity with DeleteBehavior.Restrict does not work well

我有一个 sql 服务器数据库先用代码创建。有两个表具有一对多关系。该数据库运行良好并且创建良好。 在 sql 服务器中,如果我尝试删除其中一个分类记录,则会出现错误(引用完整性限制)。这就是我希望它工作的方式。但是在ef core中,如果我删除了一个分类dbset.Remove(classification),分类被删除,customer中的分类设置为null。 我认为这就是 DeleteBehavior.ClientSetNull 的工作方式。 https://docs.microsoft.com/en-us/ef/core/saving/cascade-delete 中有注释“EF Core 2.0 中的更改”,解释了 DeleteBehavior 函数。

I have the next records:
Classification:
Id      Name
1       General
2       Others
Customers:
Id      Name        IdClassification
1       Customer A  1
2       Customer B  2
3       Customer C  <null>

public class Customer
{
    public int Id { get; set; }
    public string Name { get; set; }
    ...
    public int? IdClassification { get; set; }
    public Classification Classification { get; set; }
}

public class Classification
{
    public int Id { get; set; }
    public string Name { get; set; }
    ...
    public ICollection<Customer> Customers { get; set; }
}

public class Context : DbContext
{
    public virtual DbSet<Classification> Classifications { get; set; }
    public virtual DbSet<Customer> Customers { get; set; }

    ...

    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        modelBuilder.Entity<Classification>(
        entity => 
        {
            entity.HasKey(e => e.Id);
        });

        modelBuilder.Entity<Customer>(
        entity =>
        {
            entity.HasKey(e => e.Id);
            entity.HasIndex(e => e.IdClassification);
            ...
            // Claves foráneas
            entity.HasOne(c => c.Classification)
                .WithMany(x => x.Customers)
                .HasForeignKey(x => x.IdClassification)
                .OnDelete(DeleteBehavior.Restrict)
                .HasConstraintName("FK_Customer_Classification");
        });
    }
}

有什么方法可以防止删除ef core中的分类记录吗? (我不想检查是否有任何客户记录链接到分类,因为我必须将分类与更多表一起使用)。 提前致谢。

嗯,分类实体需要正确初始化,假设删除限制规则。

modelBuilder.Entity<Classification>()
    .HasKey(e => e.Id)
    .HasMany(e => e.Customers)
    .WithOne(e => e.Classification)
    .OnDelete(DeleteBehavior.Restrict)
    .IsRequired(true);

希望对您有所帮助。

EF Core 3.0 向 DeleteBehavior 枚举添加了几个新值 - ClientCascadeNoActionClientNoAction。不幸的是,文档没有更新(API 参考中的枚举值除外),并且 3.0 重大更改中仅提到了 ClientNoAction - DeleteBehavior.Restrict has cleaner semantics:

Old behavior

Before 3.0, DeleteBehavior.Restrict created foreign keys in the database with Restrict semantics, but also changed internal fixup in a non-obvious way.

New behavior

Starting with 3.0, DeleteBehavior.Restrict ensures that foreign keys are created with Restrict semantics--that is, no cascades; throw on constraint violation--without also impacting EF internal fixup.

Why

This change was made to improve the experience for using DeleteBehavior in an intuitive manner, without unexpected side-effects.

Mitigations

The previous behavior can be restored by using DeleteBehavior.ClientNoAction.

相关跟踪问题中包含更多信息 - 12661: Update DeleteBehavior to be more consistent and understandable

老实说,即使阅读了所有这些内容,我也不觉得它更清晰,但更令人困惑。 Restrict 似乎已过时并被 NoAction 取代,无论实际说了什么 确实 将加载的相关实体导航 property/FK 设置为 null,从而导致 SET NULL 数据库行为,正如您已经经历过的那样。

在尝试了所有这些之后,唯一符合您期望的选项是上述 ClientNoAction:

Note: it is unusual to use this value. Consider using ClientSetNull instead to match the behavior of EF6 with cascading deletes disabled.

For entities being tracked by the DbContext, the values of foreign key properties in dependent entities are not changed when the related principal entity is deleted. This can result in an inconsistent graph of entities where the values of foreign key properties do not match the relationships in the graph.

If the database has been created from the model using Entity Framework Migrations or the EnsureCreated() method, then the behavior in the database is to generate an error if a foreign key constraint is violated.

不管他们开头的注释是什么。


综上所述,只需将 Restrict 替换为 ClientNoAction 即可解决问题。不需要迁移数据库,因为此更改仅影响客户端行为。