首页 > 解决方案 > EF Core 级联引用完整性与 DeleteBehavior.Restrict 无法正常工作

问题描述

我首先使用代码创建了一个 sql server 数据库。有两个表具有一对多关系。数据库工作正常并且创建良好。在 sql server 中,如果我尝试删除其中一个分类记录,则会出现错误(参考完整性限制)。这就是我希望它工作的方式。但是在ef core中,如果我删除一个分类dbset.Remove(classification),则该分类被删除,客户中的分类设置为null。我认为这就是它应该如何工作的方式DeleteBehavior.ClientSetNullhttps://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 中的分类记录?(我不想检查是否有任何与分类相关的客户记录,因为我必须将分类与更多表一起使用)。提前致谢。

标签: ef-core-3.1

解决方案


EF Core 3.0 向DeleteBehavior枚举添加了几个新值 - ClientCascade, NoAction, ClientNoAction. 不幸的是,文档没有更新(API 参考中的枚举值除外),只有ClientNoAction在 3.0 Breaking Changes - DeleteBehavior.Restrict 中提到了更清晰的语义

旧行为

在 3.0 之前,DeleteBehavior.Restrict在数据库中创建具有语义的外键Restrict,但也以不明显的方式更改了内部修复。

新行为

从 3.0 开始,DeleteBehavior.Restrict确保使用Restrict语义创建外键——即没有级联;抛出约束冲突——同时不影响 EF 内部修复。

为什么

进行此更改是为了改善DeleteBehavior以直观方式使用的体验,而不会产生意外的副作用。

缓解措施

使用 可以恢复以前的行为DeleteBehavior.ClientNoAction

更多信息包含在相关的跟踪问题中 - 12661:更新 DeleteBehavior 使其更加一致和易于理解

老实说,即使在阅读完所有内容后,我也觉得它并不干净,但更令人困惑。Restrict似乎已过时并替换为NoAction,无论怎么说,实际上将加载的相关实体导航属性/FK 设置为null,从而导致SET NULL您已经经历过的数据库行为。

在尝试了所有这些之后,唯一符合您期望的选项是前面提到的ClientNoAction

注意:使用此值是不寻常的。考虑ClientSetNull改为使用禁用级联删除来匹配 EF6 的行为。

对于被 跟踪的DbContext实体,删除相关主体实体时,从属实体中的外键属性值不会更改。这可能会导致实体图不一致,其中外键属性的值与图中的关系不匹配。

如果数据库是使用实体框架迁移或EnsureCreated()方法从模型创建的,那么如果违反外键约束,数据库中的行为将生成错误。

不管他们在开始时的注意事项。


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


推荐阅读