简体   繁体   English

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

[英]Remove cascade on delete from a single path

Follwing my data models. 跟踪我的数据模型。 I stripped all the unimportant annotations to keep it short and clean. 我删除了所有不重要的注释,以使其简洁明了。

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; }
}

If i create it like that i will get the error: 如果我这样创建它,我将得到错误:

'Introducing FOREIGN KEY constraint 'FK_dbo.Item_dbo.Level_LevelId' on table 'Item' may cause cycles or multiple cascade paths. 在表“ Item”上引入FOREIGN KEY约束“ FK_dbo.Item_dbo.Level_LevelId”可能会导致循环或多个级联路径。 Specify ON DELETE NO ACTION or ON UPDATE NO ACTION, or modify other FOREIGN KEY constraints. 指定ON DELETE NO ACTION或ON UPDATE NO ACTION,或修改其他FOREIGN KEY约束。 Could not create constraint or index. 无法创建约束或索引。 See previous errors.' 查看先前的错误。

If i add this code the database creation works: 如果我添加此代码,则数据库创建有效:

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

But then i get this error when i delete a bubble: 但是当我删除气泡时,我得到了这个错误:

SqlException: The DELETE statement conflicted with the REFERENCE constraint "FK_dbo.Item_dbo.Level_LevelId". SqlException:DELETE语句与REFERENCE约束“ FK_dbo.Item_dbo.Level_LevelId”冲突。 The conflict occurred in database "MvBubbles1", table "dbo.Item", column 'LevelId'. 数据库“ MvBubbles1”的表“ dbo.Item”的列“ LevelId”中发生了冲突。 The statement has been terminated. 该语句已终止。

So i belive the problem is that Level is self referencing. 所以我相信问题是,级别是自引用的。 Because i have many one to many relationships and cascade on delete works everywhere except between Level and Item, the only difference is that the parent is not self referencing in the cases that work. 因为我有很多一对多的关系,并且删除和层叠操作在除Level和Item之间的所有地方都可以使用,所以唯一的区别是在可行的情况下父级不是自引用的。 I belive i just have to remove one cascade path but at the moment i can't figure out how to do this and where exactly the problem is without disabling cascade on delete out right but i don't want to disable it. 我相信我只需要删除一个级联路径,但此刻我不知道如何执行此操作,而在不禁用删除功能的情况下,问题出在哪里,但我不想禁用它。

Delete code: 删除代码:

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

The reason that you can't remove the Bubble while cascading is off, is because you still have Levels with a foreign key to the Bubble you want to remove. 级联关闭时无法删除气泡的原因是,您仍然具有要删除气泡的外键级别。

Besides, suppose you want to Remove Bubble 2. Bubble 2 has a top Level 20. 此外,假设您要删除气泡2。气泡2具有最高级别20。
Bubble 3 has a Level 21, which is a sub-level of Level 20. 气泡3的级别为21,是级别20的子级别。
Bubble 4 has a Level 22, which is a sub-level of Level 21. 气泡4具有级别22,它是级别21的子级别。

If you remove Bubble 2, should it remove the Levels of Bubbles 3 and 4? 如果删除气泡2,是否应删除气泡3和4的水平?

Let's assume that your software hasn't got these peculiarities: no circular Level references, and all Levels are from the same Bubble. 假设您的软件没有这些特性:没有循环的关卡引用,并且所有关卡都来自同一Bubble。

You could nullify all LevelIds of all Levels of the Bubble you want to remove before removing the Levels and the Bubbles: 您可以在删除“关卡”和“气泡”之前使要删除的“气泡”的所有关卡的所有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();

A proper solution 适当的解决方案

The fact that your code has to prevent circular references and Bubbles with Levels that are sub-levels of Levels from other Bubbles, should give you an inkling that your database isn't normalized enough. 您的代码必须防止循环引用和带有Levels的Bubbles属于其他Bubbles的Levels子级别,这应该使您知道您的数据库不够规范。

Consider to give your LevelCollection. 考虑给您的LevelCollection。 This would be a one-to-zero-or-one relation, or if all Levels of Bubble 2 have several things in common, consider putting these things into a LevelCollection and let your Bubble have zero or more LevelCollections (one-to-many). 这将是一对零或一对的关系,或者如果所有Bubble 2的所有关卡都有若干共同点,请考虑将这些东西放到LevelCollection中,并让您的Bubble拥有零个或多个LevelCollection(一对多)。 )。

Every LevelCollection belongs to exactly one Bubble. 每个LevelCollection都完全属于一个Bubble。 The LevelCollection has zero or more Levels. LevelCollection具有零个或多个级别。

Now it is guaranteed that if LevelCollection 42 belongs to Bubble 2, that all Levels of LevelCollection 42 belong to Bubble 2. You can remove the foreign key BubbleId from the Level. 现在可以保证,如果LevelCollection 42属于Bubble 2,则LevelCollection 42的所有级别都属于Bubble2。您可以从级别中删除外键BubbleId。

This won't prevent circular Level references, but it will prevent that Levels from one Tree belong to different bubbles 这不会阻止循环的关卡引用,但会阻止一棵树的关卡属于不同的气泡

Why cascading had to be switched off 为什么必须关闭级联

Normally if you have a one-to-many relation like a School with his many Students, when you delete a School you also want to automatically delete all its Students. 通常,如果您与一所学校有一对多关系,并且有许多学生,则在删除学校时,您还希望自动删除其所有学生。 When cascading is on, then entity framework will first remove all items with a foreign key to the item you request to move before it removes your item. 启用层叠时,实体框架将首先删除所有带有外键的项目,这些外键指向您要移动的项目,然后再删除您的项目。

This can't be done automatically with Bubbles and Levels 气泡和色阶无法自动完成

Let's Add some Bubbles and levels 让我们添加一些气泡和水平

                            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

Ok, cascading is on, let's remove Bubble 1. 好的,级联打开,让我们删除Bubble 1。

Before entity framework can do that it has to remove everything that has a foreign key to Bubble 1. So we need to remove Level 10 first, which we can't because we have to remove level 11 first. 在实体框架可以执行此操作之前,它必须删除所有具有Bubble 1外键的内容。因此,我们需要先删除10级,而不能删除,因为我们必须先删除11级。 However, Level 11 can't be removed, because Level 10 can't be removed, etc 但是,无法删除第11级,因为无法删除第10级,依此类推

We have made a small circle, but you can imaging what would happen if you have a circle with 1000 Levels. 我们已经制作了一个小圆圈,但是您可以想象一下,如果您拥有一个具有1000个色阶的圆圈,将会发生什么。

Your code will probably prevent that you create a circle, but entity framework can't prevent you to do this. 您的代码可能会阻止您创建一个圆,但是实体框架不能阻止您这样做。

Another problem: Suppose Bubble 2 has a level 20. Level 20 is a sub level of level 21, and level 21 has a foreign key to Bubble 1. If you remove Bubble 1, what should happen with the Level of Bubble 2? 另一个问题:假设气泡2的级别为20。级别20是级别21的子级别,而级别21具有到气泡1的外键。如果删除气泡1,则气泡2的级别会发生什么? Some people might say: it becomes a top level of Bubble 2, others might say: no, Bubble 2 loses his level. 有人可能会说:它变成了泡泡2的最高等级,其他人可能会说:不,泡泡2失去了他的等级。 Entity Framework can't detect what you want, so you'll have to do this yourself and switch cascading off. 实体框架无法检测到您想要的内容,因此您必须自己进行操作并关闭级联。

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

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