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


68

我想使用实体框架代码优先的链接表禁用级联删除。例如,如果许多用户具有许多角色,而我尝试删除一个角色,则除非当前没有与该角色相关联的用户,否则我希望该删除被阻止。我已经删除了我的级联删除约定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生成此删除级联?

Answers:


112

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

modelBuilder.Conventions.Remove<ManyToManyCascadeDeleteConvention>();

谢谢,这帮助了我很多。很好奇,您最终删除了两个约定还是仅删除了多对多约定?
kmehta

8
实际上,我最终只是删除了一对多约定,并有选择地为一个或两个实体重新启用了该约定。我对此的注释说,因为(与一对多不同),您不能使用Fluent API通过使用来对多对多重新启用级联删除.WillCascadeOnDelete(true)所有多对多表都具有级联或不级联。我考虑将它们全部级联在一起,以减少弊端,因为大多数时候,如果我删除使用多对多链接表链接的内容,我也希望删除链接的内容。
耶斯(Jez)

1
这似乎没有用。我的问题是,当我有一个相同类型的对象的两个属性(由该用户创建,并由该用户最后编辑。)我希望这会消除我遇到的“可能导致循环或多个级联路径”错误,但是它没有用。有什么建议吗?
Rogala 2014年

22
我不敢相信任何有主见的人都会认为级联删除是一个很好的默认设置。
Spongman

3
@frozen'no action'(失败失败)替代方法比意外删除数据要好得多。
Spongman

5

我认为,在ManyToManyCascadeDeleteConvention全球范围内关闭不是明智的选择。相反,最好仅针对相关表将其关闭。

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

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


3
“我认为在全球范围内关闭ManyToManyCascadeDeleteConvention不是一个明智的选择”,为什么?
Storm Muller '18

@StormMuller天哪,many-to-many关系可能意味着两个实体都必须存在才能具有有意义的关系。因此,如果删除了一个,则可能希望删除另一个。另外,当您要逐字删除项目或进行硬删除时,这会使您的数据库清理器更少/更少。这不是必须的情况,有时您只想删除两个实体之间的关系,又不删除它们中的任何一个
ebram khalil 18-10-14

一对一意味着都需要存在,一对多意味着必须存在“一个”数据集。如果是多对多关系,则意味着如果一个记录中的一条记录被删除,则该记录的子项可能仍会被该数据集中的其他记录使用。所以我不同意你的看法。
Storm Muller '18

我同意您的一般建议,认为这是不明智的;有些数据库使用软删除(=从数据库中没有实际删除),然后级联删除变得毫无意义,因此,不实施它们是一个明智的决定(例如,避免出现多个级联路径)。
扁平的

5

我同意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<User>(Role)

当然,这是问了问题很久以后了。因此,它可能在2012年无效。


发布前未经测试。.WillCascadeOnDelete在这里不可用。
Gert Arnold

@GertArnold,可以肯定它当时存在,尽管很难知道,因为它已经两年了。几乎可以肯定,它打算与EF6一起使用。您确定没有在EF Core上尝试吗?如果它在那里不起作用,我将不会感到惊讶。
TwainJ

不在EF4.3.1、5.0.0、6 +中。从来没有去过那里。
Gert Arnold

1
这意味着:答案是错误的。您应该将其删除。
Gert Arnold

1
您自己说的“我相信...”和以下错误的断言表明您在撰写本文时不确定,也没有努力测试您的答案。与内存不足或“有限方式”无关。如果您不想因为您宝贵的代表点而删除答案,那么至少要更正它。为什么故意将人指向错误的方向?
Gert Arnold
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.