我在系统中有一个场景,我尽力简化。我们有一个(让我们称之为)人工制品的表格,可以通过任意数量的安全角色访问人工制品,安全角色可以访问任意数量的人工制品。因此,我们在数据库中有3个表 - 一个描述人工制品,一个描述角色,一个多对多关联表将人工制品ID与角色ID相关联。
域名方面,我们有两个类 - 一个用于角色,一个用于人工制品。 artefact类具有IList属性,该属性返回可以访问它的角色列表。 (但是角色不提供获取可以访问的人工制品的属性。)
因此,人工制品的nhibernate映射包含以下内容;
<bag name="AccessRoles" table="ArtefactAccess" order-by="RoleID"
lazy="true" access="field.camelcase-underscore" optimistic-lock="false">
<key column="ArtefactID"/>
<many-to-many class="Role" column="RoleID"/>
</bag>
这一切都运行正常,如果我删除了一个人工制品,关联表会被适当地清理,删除的人工制品和角色之间的所有引用都会被删除(但是这个角色不会被删除,因为我们不想要孤儿删除)。
问题是 - 如何删除角色并让它自动清除关联表。如果我目前尝试删除一个角色,我会得到一个引用约束,因为该角色的关联表中仍有条目。成功删除角色的唯一方法是查询链接到该角色的所有人工制品,从人工制品的角色集合中删除角色,更新人工制品然后删除角色 - 效率不高或不好,特别是在非简化的系统,角色可以与任意数量的其他表/对象相关联。
我需要能够向NHibernate提示我希望每当我删除一个角色时都要清除这个关联表 - 这是否可行,如果可以的话 - 我该怎么做?
感谢您的帮助。
答案 0 :(得分:8)
由于我正在寻找这个答案并在谷歌上发现这个帖子(没有回答)我想我会发布我的解决方案。有三个表:Role,RolesToAccess(ManyToMany),Access。
创建以下映射: 访问:
<bag name="Roles" table="RolesToAccess" cascade="none" lazy="false">
<key column="AccessId" />
<many-to-many column="AccessId" class="Domain.Compound,Domain" />
</bag>
<bag name="RolesToAccess" cascade="save-update" inverse="true" lazy="false">
<key column="AccessId" on-delete="cascade" />
<one-to-many class="Domain.RolesToAccess,Domain" />
</bag>
角色:
<bag name="Accesses" table="RolesToAccess" cascade="none" lazy="false">
<key column="RoleId" />
<many-to-many column="RoleId" class="Domain.Compound,Domain" />
</bag>
<bag name="RolesToAccess" cascade="save-update" inverse="true" lazy="false">
<key column="RoleId" on-delete="cascade" />
<one-to-many class="Domain.RolesToAccess,Domain" />
</bag>
如上所述,您可以保护RolesToAccess属性,以免污染您的模型。
答案 1 :(得分:1)
你在这说:
成功删除角色的唯一方法是查询链接到该角色的所有人工制品,从人工制品的角色集合中删除角色,更新人工制品然后删除角色 - 效率不高或不太好,尤其是在在未简化的系统中,角色可以与任意数量的其他表/对象相关联。
没有必要。假设您不想映射关联表(使其成为域对象),您仍然可以使用最少的代码在两端执行删除。
假设有3个表:Role,Artifact和ArtifactAccess(链接表)。 在映射中,您只有Role和Artifact的域对象。两者都有许多协会的包。
作用:
<bag name="Artifacts" table="[ArtifactAccess]" schema="[Dbo]" lazy="true"
inverse="false" cascade="none" generic="true">
<key column="[ArtifactID]"/>
<many-to-many column="[RoleID]" class="Role" />
</bag>
工件:
<bag name="Roles" table="[ArtifactAccess]" schema="[Dbo]" lazy="true"
inverse="false" cascade="none" generic="true">
<key column="[RoleID]"/>
<many-to-many column="[ArtifactID]" class="Role" />
</bag>
如您所见,两端都指定了inverse = false。 NHibernate文档建议您选择关联的一端作为“反向”结束,但没有什么能阻止您将两者用作“控制端”。执行更新或插入时,这可以从两个方向顺利进行。当执行任一端的删除时,您会收到FK违规错误,因为关联表未更新,为true。但是你可以通过在执行删除之前将集合清除到另一端来解决这个问题,这比你做的要简单得多,如果有'this'的用法,它会查找关联的“其他”端。 ' 结束。如果这有点令人困惑,这是一个代码示例。如果您只有一个控制端,则需要执行复杂删除:
foreach(var artifact in role.Artifacts)
foreach(var role in artifact.Roles)
if(role == roleToDelete)
artifact.Roles.Remove(role)
artifact.Save();
roleToDelete.Delete();
删除角色时的操作类似于
roleToDelete.Artifacts.Clear(); //removes the association record
roleToDelete.Delete(); // removes the artifact record
这是一行额外的代码,但是这样你就不需要决定关联的哪一端是反向结束。您也不需要映射关联表以进行完全控制。
答案 2 :(得分:0)
您可以为关联表创建映射,然后在该表上调用delete,其中Role_id是您要删除的值,然后执行角色本身的删除。这样做应该相当简单。
答案 3 :(得分:0)
虽然我相信NHibernate必须提供一种方法来实现这一点而不需要在角色C#类中使用集合,但您始终可以在SQL中设置此行为。在数据库中为FK选择级联删除,它应该是自动的,只需要注意NHib的缓存。
但我强烈建议您将此作为最后一种资源。
答案 4 :(得分:0)
您需要创建从Role到Artifact
的映射。
你可以使它延迟加载,并将其映射到受保护的虚拟成员,以便它实际上永远不会被访问,但你需要在那里映射NHibernate知道它必须从{{1删除角色表}