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

时间:2016-04-04 10:43:52

标签: nhibernate

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

 ReadOnlyCache: Can't write to a readonly object ShipRepDAL.ShipRepDTO.Role

问题: 应用程序不应更新角色记录。我的代码只触及Role对象上的PersonRole集合,所以我不明白为什么Nhibernate正在更新Role记录。

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

[...]
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

以下是每个表格映射的示例:

    

<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>

PersonRole

<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>

非常感谢任何关于错误的指示!

2 个答案:

答案 0 :(得分:1)

TL; DR

除非您在映射中将bag替换为set,否则我没有明确说明您遇到麻烦的原因。

详细

bag有一种奇怪的用法,在您的情况下通常应为set。或者我错过了什么?

一个包具有非常特殊的语义,如果使用它会让我感到惊讶,因为NHibernate会在每次PersonRole更改时触及你的角色。

NHibernate reference,接近§6.2的结尾:

  

行李是无序的,无索引的集合,可能多次包含相同的元素。

PersonRolesRole上的Person个馆藏可能包含重复的PersonRole吗?

而且,有些线路仍然是关于袋子的:

  

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

由于你的行李集合不是真的,你在PersonRoleRole之间没有中间表来保存行李(table映射没有bag属性),在这种情况下,我猜是Role最终被用作包表,从而导致NHibernate在您更改PersonRoles集合时触摸它。

Improving performances中的更多细节,接近§19.5.1的结尾:

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

答案 1 :(得分:1)

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

我还是想保留这个问题,因为弗雷德里克的答案很有趣。