用“选定的”成员来建模组成员资格。数据库中的成员
在我的数据模型中,我有一个实体 Group 和另一个实体 GroupMember
。一个Group
由一个或多个GroupMember
组成,但一个GroupMember
只能同时属于一个Group
。到目前为止没有问题,在数据库中 GroupMember
有一个 Group
id 的外键。但是现在我想让其中一名成员成为“默认”或“选定”成员。应该始终只有一名选定的成员,不多也不少。
我尝试在实体框架中对此进行建模,其中具有一个 1-* 关联来建模组成员身份,以及一个 (0..1)-1 关系来保存 Group 内所选
。GroupMember
的实例
但是现在我显然遇到了问题,当插入 Group
和 GroupMember
的实例时,我收到一个错误,实体框架无法确定插入项目的顺序,因为 Group
需要有效的 GroupMember
作为默认成员,但除非不引用现有的 Group
实体,否则无法插入 GroupMember
。可以说是先有鸡还是先有蛋的问题...
最简单的方法可能是可选地建立其中一个关系,但这将消除我希望在正常数据库操作期间拥有的约束。理想情况下,实体框架应以任意顺序将数据插入数据库,并且数据库应仅在事务结束时检查约束违规情况。
另一种方法是将所选成员建模为 GroupMember 中的布尔属性“IsSelected”。但是我不确定如何确保仅使用实体框架设计器同时只有一个选定的成员(我想尽量避免直接使用数据库)。
您能否提供任何指导来处理此问题的首选方法是什么?谢谢!
In my data model I have an entity Group and another entity GroupMember
. One Group
consists of one or more GroupMembers
, but one GroupMember
can only be in one Group
at the same time. So far no problem, in the database GroupMember
has a foreign key to the Group
's id. However now I want to have one of the members to be the "default" or "selected" member. There should always be exactly one selected member not more and not less.
I tried modelling this in Entity Framework having one 1-* association to model the group membership and one (0..1)-1 relationship to save an instance of the selected GroupMember
inside of Group
.
However now I obviously have the problem, that when inserting instances of Group
and GroupMember
I get an error that entity framework cannot determine in which order to insert the items, since Group
requires a valid GroupMember
as the default member, but the GroupMember
cannot be inserted unless without referencing an existing Group
entity. A chicken-egg problem so to say...
The easiest way would probably be to make one of the relationships optionally, but this would remove a constraint that I would like to have during normal database operation. Ideally entity framework should insert the data in any order to the database and the database should check constraint violations only at the end of the transaction.
Another way would be to model the selected member as a boolean property "IsSelected" in the GroupMember. However I'm not sure how to ensure that there is only one selected member at the same time using only the entity framework designer (I want to try to avoid working with the database directly).
Can you offer any guidance what would be the preferred way to handle this? Thanks!
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
对此进行建模的正确方法是使用关联表:
I'我假装“领导者”是对群体中“特殊”人的准确描述。您应该尝试使用比“selected”更具描述性的名称。
该架构如下所示:
它表达以下有关关系的信息:
存在一个组,期间。它可能有也可能没有成员。如果它没有成员,那么根据定义它也没有领导者。它仍然存在,因为稍后可能会添加新成员。
一个人存在,就这样。一个人不会仅仅因为他/她的群体不存在而不再存在。
一个人可以是一个且仅有一个群体的成员。
一个人也可能是一个团体的领导者。一个团体一次只能有一名领导者。群组的领导者可能也可能不被视为成员。
您可能认为这种关系设计所施加的约束比您问题中提出的约束要宽松得多。你是对的。这是因为您的问题是将数据模型与业务/域需求混为一谈。
除了此模型之外,您还应该有一些业务规则,由您的应用程序强制执行,例如:
如果群组没有成员,则将其删除/停用/隐藏。
如果停用/隐藏的组获取成员,则会重新激活/显示。
一个人必须是某个团体的成员。添加新人员时必须提供此信息(不必是现有组,可以是新组)。如果一个人的成员资格组被删除,这应该会触发一个异常过程;或者,如果群组仍有成员,则不允许删除该群组。
一个团体有成员就必须有领导者。如果将新人添加到空组中,则该人将成为领导者。如果领导者(人)被删除,则应根据某些标准自动选择新的领导者,否则应触发例外流程。
为什么这是“正确”的设计?
首先是因为它准确地描绘了实体及其关系的独立性。团体和个人实际上并不相互依赖;这只是您的业务规则规定您对没有群组成员资格的人员或没有任何成员或领导者的群组不感兴趣。
更重要的是,因为索引和约束更加清晰:
UPDATE
语句。UPDATE
语句。所有其他解决方案都有一些严重的致命缺陷:
将
GroupId
放在Person
和LeaderId
上onGroup
会导致循环,除非使至少一列可为空,否则无法解决该循环。您也无法CASCADE
其中一种关系。将
GroupId
放在Person
上并将附加IsLeader
放在Person
上不允许您强制执行上限绑定(每组 1 名领导者),无需触发。实际上,从技术上讲,您可以使用过滤索引(仅限 SQL '08),但它仍然是错误的,因为IsLeader
位实际上并不指定关系,并且如果您不小心更新了GroupId
但忘记IsLeader
那么你突然让这个人成为一个完全不同的群体的领导者,并且可能违反了至多一个约束。有些人会选择将
GroupId
添加到Person
,但仍保留Leader
关联表。从概念上讲,这是一个更好的设计,但由于您可能有一个从组到个人的CASCADE
,因此您将无法在 Leader 上放置双向CASCADE
(如果您尝试,您将收到“多个级联路径”错误)。是的,我知道这需要更多的工作,并且需要您更加努力地思考您的业务规则是什么,但是相信我,这就是您想要做的。其他任何事情都只会导致痛苦。
The correct way to model this is with an association table:
I'm pretending that "leader" is an accurate description of who is "special" in the group. You should try to use a more descriptive name than "selected".
The schema looks like this:
It expresses the following information about the relationships:
A group exists, period. It may or may not have members. If it has no members, then by definition it also has no leader. It still exists, because new members might be added later.
A person exists, period. A person would not cease to exist simply because his/her group does.
A person may be a member of one and only one group.
A person may also be the leader of a group. A group can only have one leader at a time. The leader of a group may or may not be considered a member.
You may think that the constraints imposed by this relational design are significantly looser than the ones asked about in your question. And you'd be right. That's because your question is conflating the data model with the business/domain requirements.
In addition to this model you should also have several business rules, enforced by your application, such as:
If a group has no members, it is deleted/deactivated/hidden.
If a deactivated/hidden group acquires members, it is reactivated/shown.
A person must be a member of some group. This information must be supplied when a new person is added (it does not have to be an existing group, it can be a new group). If a person's membership group is deleted, this should trigger an exception process; alternatively, do not allow a group to be deleted if it still has members.
A group which has members must have a leader. If a new person is added to an empty group, that person becomes the leader. If the leader (person) is deleted, then a new leader should be automatically selected based on some criteria, or an exception process should be triggered.
Why is this the "correct" design?
First of all because it accurately portrays the independence of entities and their relationships. Groups and persons do not actually depend on each other; it is simply your business rules dictating that you are not interested in persons without a group membership or groups without any members or leaders.
More importantly because the indexing and constraints are far cleaner:
UPDATE
statement.UPDATE
statement.All of the other solutions have some serious, fatal flaw:
Putting the
GroupId
onPerson
andLeaderId
onGroup
results in a cycle that cannot be resolved except by making at least one of the columns nullable. You will also not be able toCASCADE
one of the relationships.Putting the
GroupId
onPerson
and an additionalIsLeader
onPerson
does not allow you to enforce the upper bound (1 leader per group) without a trigger. Actually, you technically can with a filtered index (SQL '08 only), but it's still wrong-headed because theIsLeader
bit does not actually designate a relationship, and if you accidentally update theGroupId
but forget aboutIsLeader
then you've suddenly just made this person the leader of an entirely different group, and probably violated the at-most-one constraint.Some people will choose to add
GroupId
toPerson
but still maintain theLeader
association table. That is a better design conceptually, but since you'll likely have aCASCADE
from Group to Person, you won't be able to put a two-wayCASCADE
on Leader as well (you'll get the "multiple cascade paths" error if you try).Yes, I know it's a little more work and requires you to think a little harder about what your business rules are, but trust me, this is what you want to do. Anything else will only lead to pain.
最简单的方法如下:
GroupMember
实体上声明一个布尔属性IsSelected
。GroupMember
类添加分部类声明(所有 EF 实体类都声明为分部类,因此可以轻松使用自定义代码扩展它们)。IsSelected
属性的“BeforeValueChanging”事件(我记不清该事件的确切名称,但您可以确定 EF 提供了类似的内容。) 。HTH!
The easiest way to do this is as follows:
IsSelected
on theGroupMember
entity.GroupMember
class (all EF entity classes are declared partial, so it's easy to extend them with custom code).IsSelected
property (I can't remember the exact name of the event from the top of my head, but you can be sure that EF provides something like that.).HTH!