views:

101

answers:

1

Hl Guys,

I am busy writing a backend administrative program for a system that exists. I have selected NHibernate for my data access solution and am fairly new to it. I am experiencing the following error in a parent/child relationship:

NHibernate.StaleStateException : Unexpected row count: 0; expected: 1

This error is caused by the fact that in my source code I add the new child object into the parent’s children collection of MeetingAdministrators. When I save the parent object I expect the children to be added as well, however an INSERT is generated only for the parent object. Nhibernate does not generate an INSERT for the child but instead attempts to UPDATE the child even though it does not exist. Thus it brings up the error message shown above. I have looked everywhere on the web and nhibernate documentation for this scenario but have not found any help. Most code involves foreign keys that are not part of the primary key, or people seem to be dealing with one-to-one or many-to-many relationships. I need to specify the mapping and code so that on insert of the parent, the children get inserted as well. Please help.

My data structure is as follows:

Meeting – parent table

  • MeetingID (pk) (int, identity)
  • Description
  • StartDate
  • IsActive
  • Venue

MeetingAdministrator – child table

  • MeetingID (pk, fk)
  • AdminNetworkID (pk) (varchar)
  • DateCreated
  • IsActive

And here is the Visual Basic .NET source:

<Serializable()> _
Public Class MeetingAdministrator

    Private _MeetingID As Integer
    Public Overridable Property MeetingID() As Integer
        Get
            Return _MeetingID
        End Get
        Set(ByVal value As Integer)
            _MeetingID = value
        End Set
    End Property

    Private _AdminNetworkID As String
    Public Overridable Property AdminNetworkID() As String
        Get
            Return _AdminNetworkID
        End Get
        Set(ByVal value As String)
            _AdminNetworkID = value
        End Set
    End Property

    Private _IsActive As Byte
    Public Overridable Property IsActive() As Byte
        Get
            Return _IsActive
        End Get
        Set(ByVal value As Byte)
            _IsActive = value
        End Set
    End Property

    Private _DateCreated As Date
    Public Overridable Property DateCreated() As Date
        Get
            Return _DateCreated
        End Get
        Set(ByVal value As Date)
            _DateCreated = value
        End Set
    End Property

    Private _LastModified As Date
    Public Overridable Property LastModified() As Date
        Get
            Return _LastModified
        End Get
        Set(ByVal value As Date)
            _LastModified = value
        End Set
    End Property

    Private _meeting As Meeting
    Public Overridable Property Meeting() As Meeting
        Get
            Return _meeting
        End Get
        Set(ByVal value As Meeting)
            _meeting = value
        End Set
    End Property

Public Overrides Function Equals(ByVal obj As Object) As Boolean Return MyBase.Equals(obj) End Function

    Public Overrides Function GetHashCode() As Integer
        Return MyBase.GetHashCode()
    End Function

End Class

Imports Iesi.Collections Imports Iesi.Collections.Generic

Public Class Meeting

    Private _MeetingID As Integer
    Private _Description As String

    Public Overridable Property MeetingID() As Integer
        Get
            Return _MeetingID
        End Get
        Set(ByVal value As Integer)
            _MeetingID = value
        End Set
    End Property

    Public Overridable Property Description() As String
        Get
            Return _Description
        End Get
        Set(ByVal value As String)
            _Description = value
        End Set
    End Property

    Private _StartDate As Date = Now
    Public Overridable Property StartDate() As Date
        Get
            Return _StartDate
        End Get
        Set(ByVal value As Date)
            _StartDate = value
        End Set
    End Property

    Private _IsActive As Byte
    Public Overridable Property IsActive() As Byte
        Get
            Return _IsActive
        End Get
        Set(ByVal value As Byte)
            _IsActive = value
        End Set
    End Property

    Private _DateCreated As Date
    Public Overridable Property DateCreated() As Date
        Get
            Return _DateCreated
        End Get
        Set(ByVal value As Date)
            _DateCreated = value
        End Set
    End Property

    Private _Venue As String
    Public Overridable Property Venue() As String
        Get
            Return _ Venue
        End Get
        Set(ByVal value As String)
            _ Venue = value
        End Set
    End Property

    Private _meetingAdministrator As ISet(Of MeetingAdministrator)
    Public Overridable Property MeetingAdministrators() As ISet(Of MeetingAdministrator)
        Get

            Return _meetingAdministrator
        End Get
        Set(ByVal value As ISet(Of MeetingAdministrator))
            _meetingAdministrator = value
        End Set
    End Property

    Public Overridable Sub AddAdministrator(ByVal meetingAdministrator As MeetingAdministrator)
        meetingAdministrator.Meeting = Me

        _meetingAdministrator.Add(meetingAdministrator)
    End Sub


    Public Sub New()
        _meetingAdministrator = New HashedSet(Of MeetingAdministrator)()

    End Sub
End Class

Here are the mapping files:

<!-- Meeting.hbm.xml -->
<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" assembly="Data"
                    namespace="Data.Domain" >

  <!-- Mapping Information -->
  <class name="Meeting"  table="Meeting" >
    <id name="MeetingID" column="MeetingID" type="int">
      <generator class="identity" />
    </id>
    <property name="Description" />
    <property name="StartDate" />
    <property name="IsActive" />
    <property name="Venue" />
    <set name="MeetingAdministrators" table="MeetingAdministrator" inverse="true"  lazy="true"  cascade="save-update"  access="property" >
      <key column="MeetingID"  foreign-key="MeetingID"  />
      <one-to-many class="Meeting"  />
    </set>
  </class>
</hibernate-mapping>

<!-- MeetingAdministrator.hbm.xml -->
<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" assembly="Data"
                    namespace="Data.Domain" >

  <!-- Mapping Information -->
  <class name="MeetingAdministrator"  table="MeetingAdministrator" >
    <composite-id>
      <key-property  name="AdminNetworkID"  column="AdminNetworkID"  type="string"  >
      </key-property>
      <key-many-to-one name="Meeting" class="Meeting" >
        <column name="MeetingID" />
      </key-many-to-one>
    </composite-id>
    <property name="IsActive" />
    <property name="DateCreated" />
  </class>
</hibernate-mapping>
+1  A: 

I'm pretty sure you'll need to add a <version/> property to your MeetingAdministrator class to have this working propertly. See this article for further discussion.

DanP
Thanks a million Dan, That worked. I followed the article and also added things like the Equals method as opposed to what I was doing, which is override equals and leave the base implementation there. But it was the <version> property that worked. It is strange to think that there is so little clear information on this tag out there
Tachi
@Tachi - be sure to override GetHashCode as well; this is important if you'll be mapping <set>s for instance...glad I could help!
DanP