c# - 使用数据库中的“选定”成员对组成员资格进行建模

标签 c# .net database entity-framework database-design

在我的数据模型中,我有一个实体组和另一个GroupMember实体。一个Group包含一个或多个GroupMembers,但一个GroupMember只能同时位于一个Group中。到目前为止没有问题,在数据库中GroupMember有一个指向Group的id的外键,但是现在我想让其中一个成员成为“默认”或“选定”成员。应该始终只有一个选定的成员不多也不少。
我尝试在实体框架中对此进行建模,其中一个1-*关联用于建模组成员关系,另一个(0..1)-1关系用于在GroupMember中保存所选Group的实例。
但是,现在我显然遇到了这样的问题,即在插入GroupGroupMember的实例时,会遇到一个错误,即实体框架不能确定插入项目的顺序,因为Group需要一个有效的GroupMember作为默认成员,但是除非引用现有的GroupMember实体,否则不能插入Group。一个鸡蛋问题,所以说…
最简单的方法可能是选择性地创建一个关系,但这将删除我希望在正常数据库操作期间具有的约束。理想情况下,实体框架应该以任何顺序将数据插入数据库,数据库应该只在事务结束时检查约束冲突。
另一种方法是将所选成员建模为groupmember中的布尔属性“isselected”。但是,我不确定如何确保只有一个选定的成员同时使用实体框架设计器(我想避免直接使用数据库)。
你能提供一些指导吗?处理这个问题的最佳方法是什么?谢谢!

最佳答案

正确的建模方法是使用association table

+-------+              +--------+                 +--------+
| Group |--------------| Member |-----------------| Person |
+-------+ 1          * +--------+ 1             1 +--------+
    | 1                                               | 1
    |                                                 |
    |                                                 |
    | 0..1                                            |
+--------+                                            |
| Leader |--------------------------------------------+
+--------+ 0..1

我假装“领导”是对团队中“特别”的人的准确描述。您应该尝试使用比“selected”更具描述性的名称。
架构如下所示:
CREATE TABLE Group
(
    Id int NOT NULL PRIMARY KEY,
    ...
)

CREATE TABLE Person
(
    Id int NOT NULL PRIMARY KEY,
    ...
)

CREATE TABLE Member
(
    PersonId int NOT NULL PRIMARY KEY
        CONSTRAINT FK_Member_Person FOREIGN KEY REFERENCES Person (Id)
            ON UPDATE CASCADE ON DELETE CASCADE,
    GroupId int NOT NULL
        CONSTRAINT FK_Member_Group FOREIGN KEY REFERENCES Group (Id)
            ON UPDATE CASCADE ON DELETE CASCADE
)
CREATE INDEX IX_Member_Group ON Member (GroupId)

CREATE TABLE Leader
(
    PersonId int NOT NULL PRIMARY KEY
        CONSTRAINT FK_Leader_Person FOREIGN KEY REFERENCES Person (Id)
            ON UPDATE CASCADE ON DELETE CASCADE,
    GroupId int NOT NULL
        CONSTRAINT FK_Leader_Group FOREIGN KEY REFERENCES Group (Id)
            ON UPDATE CASCADE ON DELETE CASCADE,
    CONSTRAINT U_Member_Group UNIQUE (GroupId)
)

它表达了关于这些关系的以下信息:
存在一个组,周期。它可能有成员,也可能没有成员。如果它没有成员,那么根据定义,它也没有领导者。它仍然存在,因为稍后可能会添加新成员。
一个人存在,一段时间。一个人不会因为他/她的团队而停止存在。
一个人可以是一个且只能是一个组的成员。
一个人也可以是一个团队的领导者。一个团队一次只能有一个领导者。一个小组的领导可以被认为是成员,也可以不被认为是成员。
您可能认为这种关系设计所施加的约束比问题中所问的约束要宽松得多。你是对的。这是因为您的问题是将数据模型与业务/域需求混为一谈。
除了此模型之外,您还应该有几个由应用程序强制执行的业务规则,例如:
如果组没有成员,则将删除/停用/隐藏该组。
如果停用/隐藏组获取成员,则会重新激活/显示该组。
一个人必须是某个组的成员。当添加新的人时,必须提供这个信息(它不必是现有的组,它可以是一个新的组)。如果删除了某人的成员组,则应触发异常过程;或者,如果组仍有成员,则不允许删除该组。
有成员的团体必须有领导。如果一个新的人被添加到一个空组中,该人将成为领导者。如果删除了领导(人员),则应根据某些条件自动选择新的领导,否则应触发异常过程。
为什么这是“正确的”设计?
首先,因为它准确地描述了实体的独立性及其关系。团体和个人实际上并不相互依赖;只是你的商业规则规定你对没有团体成员或没有任何成员或领导的团体不感兴趣。
更重要的是,索引和约束是非常干净的:
查询组成员很快。
查询一个人的成员资格很快。
询问小组组长很快。
询问同时也是领导者的人很快。
删除组将自动删除所有组成员/领导。
删除一个人将自动删除所有组成员/领导。
更改成员资格仍然是一个UPDATE语句。
更换领导层仍然是一个单一的声明。
SQL Server不会抱怨多个级联路径。
每个表最多有两个索引,在您希望被索引的列上。
您可以轻松地扩展此设计,即适应不同类型的成员。
成员/领导层的变更永远不会影响简单的查询(如按姓名查找人员)。
每个虫子都能毫不费力地处理这个问题。一般来说,您会将其视为多对多,但您可能可以将其实现为一对一可为空。
所有其他解决方案都有一些严重的致命缺陷:
UPDATE设置为GroupIdPerson设置为LeaderId将导致无法解决的循环,除非将至少一列设置为空。您也将无法Group其中一个关系。
如果将CASCADE设置为GroupId且另一个Person设置为IsLeader则不允许您在没有触发器的情况下强制执行上限(每组一个领导者)。实际上,从技术上讲,你可以使用过滤索引(仅限SQL'08),但它仍然是错误的,因为Person位实际上并不指定关系,如果你意外地更新了IsLeader但忘记了GroupId,那么你突然让这个人成为一个完全不同的组的领导者,可能违反了最多一个约束。
有些人会选择将IsLeader添加到GroupId中,但仍保留Person关联表。从概念上来说,这是一个更好的设计,但是由于您可能会有一个Leader从一个组到另一个人,您将无法在leader上也放置一个双向CASCADE(如果您尝试,您将得到“多重级联路径”错误)。
是的,我知道这是一项更艰巨的工作,需要你对你的商业规则有一点思考,但相信我,这是你想做的。其他任何事情都只会导致疼痛。

关于c# - 使用数据库中的“选定”成员对组成员资格进行建模,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/7946569/

相关文章:

c# - 保存和解析枚举集合的最快方法?

c# - MVC 5 中的 ASP.NET 成员(member)提供程序

c# - 为什么垃圾收集器只有 3 代,不多也不少?

php - 如何连接两个数据库表并从连接表中返回最低价格

database - Grails动态数据库连接?

c# - 启动新进程并杀死当前进程

c# - 如何使用 C# 编码更改特定项目的站点核心模板?

c# - 从 int 到短字符串的 2 路加密

sql - JOIN 和 UNION 有什么区别?

c# - 隐藏不可聚焦的 WPF 文本框的右键菜单