Nhibernate 更改集合属性会导致更新未触及的记录

Nhibernate 更改集合属性会导致更新未触及的记录,nhibernate,Nhibernate,我有三张桌子:Person、Role和PersonRole。角色表中的记录很少更改,因此我决定将角色表添加到NHibernate的二级缓存中。现在有几项测试失败了,他们说: ReadOnlyCache: Can't write to a readonly object ShipRepDAL.ShipRepDTO.Role 问题: 应用程序不应更新角色记录。我的代码只涉及角色对象上的PersonRole集合,因此我不理解Nhibernate为什么要更新角色记录 下面是从业务对象到个人实体的代码

我有三张桌子:Person、Role和PersonRole。角色表中的记录很少更改,因此我决定将角色表添加到NHibernate的二级缓存中。现在有几项测试失败了,他们说:

 ReadOnlyCache: Can't write to a readonly object ShipRepDAL.ShipRepDTO.Role
问题: 应用程序不应更新角色记录。我的代码只涉及角色对象上的PersonRole集合,因此我不理解Nhibernate为什么要更新角色记录

下面是从业务对象到个人实体的代码映射示例。此示例添加了一个新角色:

[...]
var pr = new PersonRole
{
    Person = person,
    Role = role
};
person.PersonRoles.Add(pr);
role.PersonRoles.Add(pr); //If I comment out this line, it works - but then the Session will be out of sync
[...]
new PersonService().SaveOrUpdate(person); //Flush
下面是每个表的映射示例:


[...]
[...]
角色


[...]
[...]       
角色


[...]
[...]
[...]
任何关于错误的提示都将不胜感激

TL;博士 我看不出您遇到麻烦的明确原因,除了您应该在映射中将
bag
替换为
set
之外

细节
bag
有一个奇怪的用法,在您的情况下应该正常设置
set
。还是我遗漏了什么

一个包有一个非常特殊的语义,如果像你这样使用它会导致NHibernate在每次
PersonRole
更改时都会触及你的角色,我不会感到惊讶

见§6.2结尾处:

一个包是一个无序的、未编制索引的集合,可能多次包含同一元素

您的
PersonRole
集合在
Role
Person
上是否真的包含重复的
PersonRole

此外,有些线路仍然是关于行李的:

NHibernate无法单独创建、删除或更新行,因为没有可用于标识单个行的键

由于您的行李收集不是真正的一个,您在
PersonRole
Role
之间没有中间表来存放您的行李(在
bag
mapping上没有
table
属性),在这种情况下,我想这就是
Role
,它最终被用作行李表,因此,当您更改
PersonRoles
collection时,会导致NHibernate触摸它

更多详情见§19.5.1末尾:

袋子是最坏的情况。由于包允许重复的元素值,并且没有索引列,因此不能定义主键。NHibernate无法区分重复行。NHibernate通过完全删除(在一次删除中)并在集合发生更改时重新创建集合来解决此问题。这可能是非常低效的

TL;博士 我看不出您遇到麻烦的明确原因,除了您应该在映射中将
bag
替换为
set
之外

细节
bag
有一个奇怪的用法,在您的情况下应该正常设置
set
。还是我遗漏了什么

一个包有一个非常特殊的语义,如果像你这样使用它会导致NHibernate在每次
PersonRole
更改时都会触及你的角色,我不会感到惊讶

见§6.2结尾处:

一个包是一个无序的、未编制索引的集合,可能多次包含同一元素

您的
PersonRole
集合在
Role
Person
上是否真的包含重复的
PersonRole

此外,有些线路仍然是关于行李的:

NHibernate无法单独创建、删除或更新行,因为没有可用于标识单个行的键

由于您的行李收集不是真正的一个,您在
PersonRole
Role
之间没有中间表来存放您的行李(在
bag
mapping上没有
table
属性),在这种情况下,我想这就是
Role
,它最终被用作行李表,因此,当您更改
PersonRoles
collection时,会导致NHibernate触摸它

更多详情见§19.5.1末尾:

袋子是最坏的情况。由于包允许重复的元素值,并且没有索引列,因此不能定义主键。NHibernate无法区分重复行。NHibernate通过完全删除(在一次删除中)并在集合发生更改时重新创建集合来解决此问题。这可能是非常低效的


经过进一步调查,根本原因是一个拦截器。此处发布了一个新问题:


我仍然想继续回答这个问题,因为弗雷德里克的回答很有趣。

经过进一步调查,根本原因是一个拦截器。此处发布了一个新问题:


我仍然想继续回答这个问题,因为弗里德里克的回答很有趣。

谢谢你的回答!我怀疑bag vs set是个问题,但我会记住bag对性能的影响。看看我的编辑,我相信拦截者是罪魁祸首。谢谢你的回复!我怀疑bag vs set是个问题,但我会记住bag对性能的影响。看看我的编辑,我相信拦截者是罪魁祸首。你的问题现在完全不同了。通过问一个新问题而不是编辑这个问题,你会有更多的机会得到答案。因此,最好将此版本还原为其原始版本,并提出另一个问题。(你也可以考虑将初始问题的实际原因(拦截器)添加到这里作为答案,或者如果你觉得这是一个太窄的情况,删除整个事件。)是的,你是对的。我恢复到原来的版本,并在下面的回答中添加了一个指向新问题的链接。我仍然想继续提问,因为你的回答很有趣。你的问题现在完全不同了。通过问一个新问题,你会有更多的机会得到答案
<class name="Person" select-before-update="true" optimistic-lock="version" batch-size="25">
    [...]
    <version name="ModifiedDate" type="Timestamp" generated="always" />
    [...]
    <bag name="PersonRoles" inverse="true" cascade="all-delete-orphan">
        <key column="PersonID"/>
        <one-to-many class="PersonRole" />
    </bag>
</class>
<class name="PersonRole" select-before-update="true" optimistic-lock="version" batch-size="25">
    [...]
    <version name="ModifiedDate" type="Timestamp" generated="always" />
    [...]       
    <many-to-one name="Person" column="PersonID" />
    <many-to-one name="Role" column="RoleID" />

</class>
<class name="Role" select-before-update="true" optimistic-lock="version" batch-size="25">
    [...]
    <version name="ModifiedDate" type="Timestamp" generated="always" />
    [...]
    <bag name="PersonRoles" inverse="true">
        <key column="RoleID"/>
        <one-to-many class="PersonRole" />
    </bag>
    [...]
</class>