如何在 EF 代码优先中禁用链接表的级联删除?

Posted

技术标签:

【中文标题】如何在 EF 代码优先中禁用链接表的级联删除?【英文标题】:How to disable cascade delete for link tables in EF code-first? 【发布时间】:2012-11-22 05:29:17 【问题描述】:

我想禁用具有实体框架代码优先的链接表的级联删除。例如,如果许多用户有多个角色,而我尝试删除一个角色,我希望阻止该删除操作除非当前没有与该角色关联的用户。我已经在OnModelCreating 中删除了级联删除约定:

protected override void OnModelCreating(DbModelBuilder modelBuilder) 
    ...
    modelBuilder.Conventions.Remove<OneToManyCascadeDeleteConvention>();

然后我设置了用户角色链接表:

modelBuilder.Entity<User>()
    .HasMany(usr => usr.Roles)
    .WithMany(role => role.Users)
    .Map(m => 
        m.ToTable("UsersRoles");
        m.MapLeftKey("UserId");
        m.MapRightKey("RoleId");
    );

然而,当 EF 创建数据库时,它会为外键关系创建一个删除级联,例如。

ALTER TABLE [dbo].[UsersRoles]  WITH CHECK ADD  CONSTRAINT [FK_dbo.UsersRoles_dbo.User_UserId] FOREIGN KEY([UserId])
REFERENCES [dbo].[User] ([UserId])
ON DELETE CASCADE
GO

ALTER TABLE [dbo].[UsersRoles]  WITH CHECK ADD  CONSTRAINT [FK_dbo.UsersRoles_dbo.Role_RoleId] FOREIGN KEY([RoleId])
REFERENCES [dbo].[Role] ([RoleId])
ON DELETE CASCADE
GO

如何阻止 EF 生成此删除级联?

【问题讨论】:

【参考方案1】:

我得到了答案。 :-) 因为ManyToManyCascadeDeleteConvention 而创建了这些级联删除。您需要删除此约定以防止它为链接表创建级联删除:

modelBuilder.Conventions.Remove<ManyToManyCascadeDeleteConvention>();

【讨论】:

感谢这对我帮助很大。很好奇,您最终是删除了 BOTH 约定还是只删除了 Many to Many 约定? 实际上,我刚刚删除了一对多约定,并有选择地为一两个实体重新启用它。我关于它的注释说,因为(与一对多不同)您不能使用 Fluent API 通过使用 .WillCascadeOnDelete(true)all重新启用多对多的级联删除> 多对多表要么必须级联,要么不能级联。我认为让它们 all 级联是害处较轻的,因为大多数时候,如果我删除使用多对多链接表链接的东西,我希望它链接的东西是也删了。 这似乎不起作用。我的问题是当我有相同类型的对象的两个属性(由该用户创建并由该用户最后编辑)时。我希望这会消除我得到的“可能导致循环或多个级联路径”错误,但是它没有用。有什么建议吗? 我无法相信任何头脑正常的人会认为级联删除是一个很好的默认设置。 @frozen '无操作'(因错误失败)替代方案比意外删除数据更好。【参考方案2】:

我认为全局关闭ManyToManyCascadeDeleteConvention 不是一个明智的选择。相反,最好将其关闭仅针对相关的表。

这可以通过为属性 cascadeDelete 编辑生成的迁移文件来实现。例如:

AddForeignKey("dbo.UsersRoles", "UserId", "dbo.User", "UserId", cascadeDelete: false);

【讨论】:

“我认为全局关闭 ManyToManyCascadeDeleteConvention 不是一个明智的选择”为什么? @StormMuller imho, many-to-many relationship 可能意味着两个实体都需要存在才能建立有意义的关系;因此,如果您删除了一个,您可能希望删除另一个。此外,当您想要删除字面意思或硬删除 项时,这将使您的数据库更干净/更少。这不是必须的情况,有时你只想删除两个实体之间的关系,但是而不删除它们中的任何一个 一对一意味着两者都需要存在,一对多意味着“一个”数据集必须存在。如果是多对多关系,这意味着如果一个数据集中的一条记录被删除,它的子项可能仍被数据集中的其他记录使用。所以我不得不不同意你的观点。 虽然我同意你的一般建议,但这是不明智的;有些数据库使用了软删除(= 没有从数据库中实际删除),然后级联删除变得毫无意义,因此不实施它们可能是一个明智的决定(例如,避免多个级联路径的问题)。【参考方案3】:

我同意 Ebram Khalil 的观点,即为一张桌子关闭它是一个不错的选择。但是,我喜欢尽可能接近自动构建的迁移,所以我会在 OnModelCreating 中进行设置:

modelBuilder.Entity<User>()
    .HasMany(usr => usr.Roles)
    .WithMany(role => role.Users)
    .Map(m => 
        m.ToTable("UsersRoles");
        m.MapLeftKey("UserId");
        m.MapRightKey("RoleId");
    )
    .WillCascadeOnDelete(false);

我相信这会保留另一个方向的删除,因此如果两者都需要被阻止(在此示例中有意义),则需要以 Entity&lt;User&gt;(Role) 开头进行类似的调用

当然,这是在提出问题很久之后才出现的。所以它可能在 2012 年无效。

【讨论】:

发布前未经测试。 .WillCascadeOnDelete 在这里不可用。 @GertArnold,很确定它当时存在,虽然很难知道,因为它已经有两年了。这几乎肯定是打算与 EF6 一起使用的。你确定你没有在 EF Core 上尝试过吗?如果它在那里不起作用,我一点也不感到惊讶。 EF4.3.1、5.0.0、6+ 中没有。没去过。 这意味着:答案是错误的。你应该删除它。 您自己的话“我相信...”以及随后的错误断言表明您在撰写本文时不确定并且没有努力测试您的答案。与失败的记忆或“有限的时尚”无关。如果您因为宝贵的代表点数而不愿意删除答案,那么至少更正它。为什么要故意把人们引向错误的方向?

以上是关于如何在 EF 代码优先中禁用链接表的级联删除?的主要内容,如果未能解决你的问题,请参考以下文章

如何更新链接到多个表的 FK - 更新时的级联

使用共享主键关联时,EF 4.1 Code First 中的级联删除规则

sql中如何实现级联表的操作

关于mysql的级联删除(之前好多人咨询过我)

如何实现关系表的级联删除(ON DELETE CASCADE的用法)

关于数据的级联删除和更新