Search code examples
nhibernatemergesaveparent-childcomposite-id

NHibernate: child composite-id is not updated when using merge()


Mapping:

    <class name="PhoneTypeTest" lazy="false" table="PhoneType">
  <cache usage="read-write"/>
  <id name ="Id" type="Int32" unsaved-value="0">
    <generator class="identity"/>
  </id>

  <bag name="Resources" table="PhoneTypeResource" lazy="false" cascade="all" inverse="true">
    <key column="PhoneTypeId" />
    <one-to-many class="PhoneTypeTestResource" not-found="ignore"/>
  </bag>  

</class>

<class name="PhoneTypeTestResource" lazy="false" table="PhoneTypeResource">
  <composite-id class="CCCC.ResourcesCompositeKey, DDDD" name="Id">
    <key-property name="OwnerId" column="PhoneTypeId"/>
    <key-property name="CultureId"/>
  </composite-id>
  <property name="Name"/>
</class>

Entities:

public class PhoneTypeTest
{
    public PhoneTypeTest()
    {
        Resources = new List<PhoneTypeTestResource>();
    }

    public virtual int Id { get; set; }
    public virtual IList<PhoneTypeTestResource> Resources { get; set; }
}

public class PhoneTypeTestResource
{
    public virtual ResourcesCompositeKey Id { get; set; }
    public virtual string Name { get; set; }
}

Unit test:

        var ent = new PhoneTypeTest();
        ent.Resources.Add(new PhoneTypeTestResource { Id = new ResourcesCompositeKey { CultureId = En, OwnerId = 0 }, Name = "Name" });

        Session.Merge(ent);
        Session.Flush();
        Session.Clear();

SQL generated by nHib:

-- statement #1
INSERT INTO PhoneType
DEFAULT VALUES


select SCOPE_IDENTITY()


-- statement #2
SELECT phonetypet0_.PhoneTypeId as PhoneTyp1_61_0_,
       phonetypet0_.CultureId   as CultureId61_0_,
       phonetypet0_.Name        as Name61_0_
FROM   PhoneTypeResource phonetypet0_
WHERE  phonetypet0_.PhoneTypeId = 0 /* @p0 */
       and phonetypet0_.CultureId = 'en' /* @p1 */

-- statement #3
INSERT INTO PhoneTypeResource
           (Name,
            PhoneTypeId,
            CultureId)
VALUES     ('Name' /* @p0 */,
            0 /* @p1 */,
            'en' /* @p2 */)

-- statement #4
ERROR: 
Could not synchronize database state with session

SO, as you can see, the problem is that nHib does NOT update child a composite-id after its parent was saved, and the attempt to save children fails. Why?? How can I make nHib to update these ids? Also, if I use SaveOrUpdate() instead Merge(), it works fine!! But I must use merge. Please help!


Solution

  • I ended up manually updating child ids after saving - just another workaround of nHibernate's bug. Do not use composite ids or, even better solution, do not use nHibernate..