【问题标题】:Delete cascade a tree in EF core (SQL Server)删除 EF 核心 (SQL Server) 中的级联树
【发布时间】:2021-08-22 17:29:10
【问题描述】:

我有以下代码:

    public class A
    {
        public Guid Id { get; set; }
        public List<A> AList { get; set; }
    }

使用fluent Api配置:

            modelBuilder
                .Entity<A>()
                .HasMany(x => x.AList)
                .WithOne()
                .OnDelete(DeleteBehavior.Cascade);

创建迁移工作正常,但在执行“更新数据库”时 我收到以下错误:

“在表'A'上引入FOREIGN KEY约束'FK_A_A_AId'可能会导致循环或多个级联路径。指定ON DELETE NO ACTION或ON UPDATE NO ACTION,或修改其他FOREIGN KEY约束。 无法创建约束或索引。查看以前的错误。”

那么如何在 EF Core 中删除级联树?

【问题讨论】:

  • 您使用的是什么数据库管理系统?在 sql server 中有一个可以在关系之间定义的删除级联。
  • 如果您查看错误源,您会发现它来自 SqlServer。所以这是 SqlServer 数据库限制,而不是 EF Core。

标签: c# sql-server entity-framework-core .net-5 ef-core-3.0


【解决方案1】:

您不能“自动”级联删除;您必须递归地删除客户端代码中的子记录,或使用递归 CTE 在数据库中创建 INSTEAD OF DELETE 触发器以查询和删除所有子记录

在客户端代码中递归删除子级的答案:

Implementing Cascade Delete in a self referencing table in EF Core 2

触发器的答案:

On delete cascade for self-referencing table

或者,将触发器逻辑实现为存储过程,并将 EFCore 配置为使用该存储过程进行删除(不是像 EF6 那样为MapToStoredProcedures 构建的功能,因此需要付出一些努力:

EF Core - What is MapToStoredProcedures replacement in EF Core 3.1 or 5

【讨论】:

  • 你不能“自动”删除...其实你可以,在一些数据库中,但是在著名的SqlServer中不行多年来一直拥有并保持这种限制。
  • @IvanStoev “切换到不同的 RDBMS”是解决这个小不便问题的一个相当激进的解决方案,你不是说吗?
  • 当然没有。如果你有很多表和关系,这不是那么“轻微的不便”,但这不是重点。此线程中的任何地方都没有提到 SqlServer - 既不是在标签中,也不是在问题或答案中。答案给人的印象是通常应该(总是)使用提供的解决方法,而问题是纯 SqlServer 特定的。
  • 错误是MSSQLSERVER_1785;很明显,张贴者正在使用 SQL Server 给任何有处理该问题的经验来回答问题的人,并且任何搜索此问题的答案的人都在使用 SQL Server(因为错误消息是特定于 SQL Server 的)。因此,唯一的其他“解决方案”是建议他们不要使用 SQL Server,我们已经说明了为什么这不合适。但为了进一步澄清,我编辑了问题主题和标签以指定SQL Server
  • 再一次,这不是反对答案,而是它的范围(现在还可以)。您对回答者的看法是正确的,但对于寻找问题的人则不然。如果他们知道这一点,他们就不会是posting one and the same question so many times。这提醒我这个问题是一个公然的重复,因此应该这样关闭而不是回答。但无论如何,感谢您的努力。
【解决方案2】:

按照当前模型的方式,A 的同一个实例可以在层次结构中出现多次。这甚至会阻止数据库引擎进行级联删除。

另一种方法是(取决于您的要求)为您的实体添加“父”外键属性,如下所示:

public class A
{
    public Guid Id { get; set; }
    
    public Guid? ParentId { get; set; }

    public A Parent { get; set; }
    
    public List<A> Children { get; set; }
}

在您的模型构建器中...

modelBuilder.Entity<A>
    .HasKey(x => x.Id);

modelBuilder.Entity<A>
    .HasMany(x => x.Children)
    .WithOne(x => x.Parent)
    .HasForeignKey(x => x.ParentId)
    .OnDelete(DeleteBehavior.Cascade);

如果您确实需要层次结构中某个节点的完全相同的实例出现在层次结构中的多个位置,则需要一些额外的建模才能使其工作。

【讨论】:

  • 这与 OP 没有什么不同(只是 FK 名称不同,并且使用显式 FK 属性而不是 shadow),因此具有相同的确切问题(不能使用 Cascade 删除行为)。
猜你喜欢
  • 1970-01-01
  • 2011-03-25
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多