繁体   English   中英

从单个路径删除时删除级联

[英]Remove cascade on delete from a single path

跟踪我的数据模型。 我删除了所有不重要的注释,以使其简洁明了。

public class Bubble
{
    public int Id { get; set; }
    public string Name { get; set; }
    public ICollection<Level> Levels { get; set; }
}

public class Level
{
    public int Id { get; set; }
    public int BubbleId { get; set; }
    [ForeignKey("ParentLevel")]
    public int? LevelId { get; set; }

    public string Name { get; set; }

    public Level ParentLevel { get; set; }
    public Bubble Bubble { get; set; }

    public ICollection<Level> Levels { get; set; }
    public ICollection<Item> Items { get; set; }
}

public class Item
{
    public int Id { get; set; }
    public int LevelId { get; set; }

    public string Name { get; set; }

    public Level Level { get; set; }
}

如果我这样创建它,我将得到错误:

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

如果我添加此代码,则数据库创建有效:

modelBuilder.Entity<Item>()
    .HasRequired(i => i.Level)
    .WithMany(l => l.Items)
    .HasForeignKey(i => i.LevelId)
    .WillCascadeOnDelete(false);

但是当我删除气泡时,我得到了这个错误:

SqlException:DELETE语句与REFERENCE约束“ FK_dbo.Item_dbo.Level_LevelId”冲突。 数据库“ MvBubbles1”的表“ dbo.Item”的列“ LevelId”中发生了冲突。 该语句已终止。

所以我相信问题是,级别是自引用的。 因为我有很多一对多的关系,并且删除和层叠操作在除Level和Item之间的所有地方都可以使用,所以唯一的区别是在可行的情况下父级不是自引用的。 我相信我只需要删除一个级联路径,但此刻我不知道如何执行此操作,而在不禁用删除功能的情况下,问题出在哪里,但我不想禁用它。

删除代码:

db.Bubbles.Remove(bubble);
db.SaveChanges();

级联关闭时无法删除气泡的原因是,您仍然具有要删除气泡的外键级别。

此外,假设您要删除气泡2。气泡2具有最高级别20。
气泡3的级别为21,是级别20的子级别。
气泡4具有级别22,它是级别21的子级别。

如果删除气泡2,是否应删除气泡3和4的水平?

假设您的软件没有这些特性:没有循环的关卡引用,并且所有关卡都来自同一Bubble。

您可以在删除“关卡”和“气泡”之前使要删除的“气泡”的所有关卡的所有LevelIds无效:

// we want to remove Bubble 2
var levelsToRemove = dbContext.Levels.Where(level => level.BubbleId == 2).ToList();
// nullify all levelIds:
foreach (var levelToRemove in levelsToRemove)
{
    levelToRemove.LevelId = null;
}
// TODO: maybe we need an extra SaveChanges

// Remove the Levels and the Bubble:
dbContext.Levels.RemoveRange(levelsToRemove);

var bubbleToRemove = dbContext.Find(2); // TODO: exception if not found
dbContext.Bubbles.Remove(bubbleToRemove);
dbContext.SaveChanges();

适当的解决方案

您的代码必须防止循环引用和带有Levels的Bubbles属于其他Bubbles的Levels子级别,这应该使您知道您的数据库不够规范。

考虑给您的LevelCollection。 这将是一对零或一对的关系,或者如果所有Bubble 2的所有关卡都有若干共同点,请考虑将这些东西放到LevelCollection中,并让您的Bubble拥有零个或多个LevelCollection(一对多)。 )。

每个LevelCollection都完全属于一个Bubble。 LevelCollection具有零个或多个级别。

现在可以保证,如果LevelCollection 42属于Bubble 2,则LevelCollection 42的所有级别都属于Bubble2。您可以从级别中删除外键BubbleId。

这不会阻止循环的关卡引用,但会阻止一棵树的关卡属于不同的气泡

为什么必须关闭级联

通常,如果您与一所学校有一对多关系,并且有许多学生,则在删除学校时,您还希望自动删除其所有学生。 启用层叠时,实体框架将首先删除所有带有外键的项目,这些外键指向您要移动的项目,然后再删除您的项目。

气泡和色阶无法自动完成

让我们添加一些气泡和水平

                            Id | Name
Add Bubble with name A  =>  1  |  A

                                                  Id | BubbleId | LevelId
Add Level without Parent for Bubble 1         =>  10 |    1     |  null
Add sub Level of Level 10 Parent for Bubble 2 =>  11 |    1     |   10

Now give Level 10 a new LevelId               =>  10 |    1     |   11

好的,级联打开,让我们删除Bubble 1。

在实体框架可以执行此操作之前,它必须删除所有具有Bubble 1外键的内容。因此,我们需要先删除10级,而不能删除,因为我们必须先删除11级。 但是,无法删除第11级,因为无法删除第10级,依此类推

我们已经制作了一个小圆圈,但是您可以想象一下,如果您拥有一个具有1000个色阶的圆圈,将会发生什么。

您的代码可能会阻止您创建一个圆,但是实体框架不能阻止您这样做。

另一个问题:假设气泡2的级别为20。级别20是级别21的子级别,而级别21具有到气泡1的外键。如果删除气泡1,则气泡2的级别会发生什么? 有人可能会说:它变成了泡泡2的最高等级,其他人可能会说:不,泡泡2失去了他的等级。 实体框架无法检测到您想要的内容,因此您必须自己进行操作并关闭级联。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM