我正在为一个小型学术项目创建一个基本的CMS,我想用它来稍后在代码中练习设计模式,我开始设计数据库。
简单的要求是"容器"某种形式将包含" Pages"或"控制"。也是一个" Page"将包含" Controls"。想想几个标签页面的容器就像容器一样。因此,当保存某些内容时,只需在容器上调用save即可立即保存所有内容。
无论如何,我有3张桌子。 " Container"," Page"和"控制"。
想想一个"容器"就像屏幕上出现的所有东西的持有者一样。如果需要,它可以拆分成其他较小的容器(用于宽屏幕)。
想想一个" Page"喜欢某种形式。允许用户放置用于收集数据的控件的东西。
想想"控制"像标签,文本框,按钮等。有各种类型的控件(图中未显示),但"控件" table将保存任何给定控件类型的多个实例。例如,具有5个标签和5个文本框的表单将在控件表中具有10个对应条目 - 每种控件类型有5个条目。因此,控件是控件类型的单个实例,将具有唯一的ID,并且只能使用一次。
如果删除了容器,我希望它能够级联并删除相关的页面和控件。
同样,如果页面被删除,我希望它能够级联并删除该页面上的控件。
我有两个问题要解决。
问题1:控件可以属于容器或页面,但不能同时属于两者。基本上,控件只能有一个所有者或父级。与页面相同。但是对于控件,因为它可以放在容器或页面上,我必须将它与2个关系连接起来,并在PageId和ControlId列上创建一个约束,以确保只有其中一个不为null。我已经成功完成了,但我想知道是否有更好的方法?
问题2:因为我使用级联更新和3个表删除。我收到一个错误:
介绍FOREIGN KEY约束' FK_Pages_Containers'桌子上 '页'可能会导致循环或多个级联路径。指定ON DELETE 无动作或更新无动作,或修改其他外键 约束
我想要属于页面或容器的控件,如果页面或容器被删除,则删除属于容器的页面。所以,在我看来,我需要级联更新和删除。所以这个错误迫使我以一种会留下孤儿的方式改变我的关系要求。
作为旁注,容器也可以包含其他容器。这在大型宽屏幕上可能非常有用,可以分为两个部分显示表单(页面),例如左侧一个,右侧一个,并且仍然可以通过单击父级上的保存按钮来保存所有内容。
那我怎么能克服这个呢?我在设计中忽略了什么吗?
为清楚起见,这是ER图:
由于上述错误,您可以从星星(*)中看到它尚未保存。我的第一个目标是使数据库正确。我首先不喜欢实体框架代码,我想首先使用实体框架数据库,或者根据需要使用替代ORM来实现它。但我希望数据库处理数据的完整性。
欢迎任何建议。
答案 0 :(得分:0)
根据问题的评论。我试了几个解决方案。这是我提出的目前似乎效果最好的一个。我刚刚发布这个以供我自己参考,也许它会帮助其他人。所以我仍然欢迎任何其他改进建议。
首先,我删除了所有关系并创建了一个容器类型表。我链接到容器管理器表。然后,我将容器和页面表以1:1的关系排列到容器管理表。这背后的逻辑是我目前有两种类型的容器,但后来我可以添加更多容器。
在容器类型中,我添加了“Container”和“Page”的数据,然后将其引用回容器管理器表。我在容器管理器表中添加了一个附加字段,以显示容器或页面的父级是谁。这是一种自我引用的关系。它稍微简化了容器和页面表。
接下来,我用控制表重复了这个过程。它稍微复杂但是同样的想法。它只是非常好地从控件中拆分容器,然后让管理表链接到“管理”事物。
从那里,只需将容器父级添加到控制管理器表并将其与容器管理器表相关联即可。所有的关系都是级联的,没有问题。如您所见,管理表与控件,容器和页面表的1:1关系相结合,提供了特定于实例的信息,便于管理。我真的很喜欢我不需要像我在原始解决方案中那样构建任何约束,以确保只填充2个字段中的一个,因为约束不明显。但是,我注意到技术上可以直接将链接的1:1表中的数据组合到管理表中,并进一步简化图表。我可能会这样做,因为查看2个表比5更容易,更快。我很想听到人们对此的看法。
一般用例是我们事先知道容器或控件的类型。因此,我们可以选择要放在页面或容器上的控件类型。此信息将数据添加到管理表,并为正在使用的容器,页面或控件的实例生成唯一的Id。然后我们使用该Id来创建用户将看到的实际容器,页面或控件实例。显然保存控件后面的数据是另一个问题,不是问题的一部分所以我不会在这里讨论这个问题。
感谢D Mayuri,他启发了这种方法,是关于分离关系的帮助和评论。我不确定这是不是他打算如何做到这一点。如果他发表自己的答案,我当然会接受并给予他信任。