关注我的数据模型。我剥离了所有不重要的注释,以使其简洁明了。
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是自引用的。因为我有很多一对多的关系,并且删除和层叠操作在除Level和Item之间的所有地方都可以使用,所以唯一的区别是在可行的情况下父级不是自引用的。我相信我只需要删除一个级联路径,但此刻我不知道如何执行此操作,而在不禁用删除功能的情况下,问题出在哪里,但我不想禁用它。
删除代码:
db.Bubbles.Remove(bubble);
db.SaveChanges();
答案 0 :(得分:0)
在级联关闭时无法删除气泡的原因是,您仍然具有要删除气泡的外键的关卡。
此外,假设您要删除气泡2。气泡2的最高级别为20。
气泡3具有21级,它是20级的子级别。
气泡4的级别为22,是级别21的子级别。
如果您删除气泡2,是否应该删除气泡3和4的水平?
让我们假设您的软件没有这些特性:没有循环的关卡引用,并且所有关卡都来自同一Bubble。
您可以在删除“关卡”和“气泡”之前使要删除的气泡的所有关卡的所有LevelId无效:
// 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失去了他的等级。实体框架无法检测到您想要的内容,因此您必须自己进行操作并关闭级联。