带有DeleteBehavior.Restrict的EF核心级联参照完整性无法正常工作

时间:2020-02-27 16:44:09

标签: ef-core-3.1

我首先使用代码创建了一个sql服务器数据库。有两个表具有一对多的关系。该数据库可以正常工作,并且创建良好。 在sql server中,如果我尝试删除分类记录之一,则会收到错误消息(参照完整性限制)。这就是我希望它起作用的方式。但是在ef core中,如果我删除一个分类dbset.Remove(classification),则会删除该分类,并将客户中的分类设置为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中的分类记录删除? (我不想检查是否有任何与分类链接的客户记录,因为我必须将分类与更多表一起使用)。 预先感谢。

2 个答案:

答案 0 :(得分:1)

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

旧行为

在3.0之前,DeleteBehavior.Restrict使用Restrict语义在数据库中创建了外键,但也以非显而易见的方式更改了内部修正。

新行为

从3.0开始,DeleteBehavior.Restrict确保使用Restrict语义创建外键,即没有级联;违反约束条件-不会影响EF内部修复。

为什么

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

缓解措施

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

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

老实说,即使阅读完所有内容,我也觉得它不干净,但更加令人困惑。 Restrict似乎已经过时,并被NoAction取代,无论实际上怎么说,确实将已加载的相关实体的导航属性/ FK设置为​​null,因此导致SET NULL的数据库行为已经发生。

尝试了所有方法之后,唯一可以满足您期望的选项就是上述ClientNoAction

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

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

如果数据库是使用Entity Framework Migrations或EnsureCreated()方法从模型创建的,那么如果违反了外键约束,则数据库中的行为将产生错误。

不管他们在一开始的音符如何。


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

答案 1 :(得分:0)

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

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

希望这会有所帮助。