Wednesday, January 26, 2011

NHibernate.TransientObjectException: object references an unsaved transient instance

***Edit: please see the next post where i explained this behavior.

When we flush and commit an ISession in Nhibernate, it goes to database to flush all changes even if you just make nothing but some select queries***. Here is an experimental test case and its result:
        [Test]
        private void CanGetDineInTableByGroup()
        {           
            TableGroup tableGroup =      this._dineInTableModel.GetTableGroupById(
                    new Guid("11111111-1111-1111-1111-111111111111"));
var dineInTables = this._dineInTableModel.GetTablesByGroup(tableGroup);

            this._dineInTableModel.CloseConversation();

            Assert.IsNotNull(tableGroup);
            Assert.IsNotNull(dineInTables);           
           
        }

Here is the result:

*** ConsoleOutput ***
NHibernate: SELECT tablegroup0_.TableGroupId as TableGro1_0_0_, tablegroup0 …
NHibernate: select dineintabl0_.DineInTableId as DineInTa1_3_, dineintabl0 …

NHibernate: UPDATE DineInTable SET RowVer = @p0, DineInTableName, …
 NHibernate: UPDATE DineInTable SET RowVer = @p0, DineInTableName = @p1,…
NHibernate: UPDATE DineInTable SET RowVer = @p0, DineInTableName = @p1,… 
NHibernate: UPDATE DineInTable SET RowVer = @p0, DineInTableName = @p1,… 

I am using uNHAddins here. CloseConversation is a convenient method to call session.Flush() and transaction.Commit() entirely controlled by uNHAddins.

As you can see, there are four update queries.  Anyone knows why, please leave a comment.

Another important “gotcha “, after closing a conversation/flush & commit, if you try to execute query in a different Session with detached object of previous Session you will probably get an error like this:


        [Test]
        private void CanGetDineInTableByGroup()
        {
           
            TableGroup tableGroup = this._dineInTableModel.GetTableGroupById(
                    new Guid("11111111-1111-1111-1111-111111111111"));
var dineInTables = this._dineInTableModel.GetTablesByGroup(tableGroup);

            this._dineInTableModel.CloseConversation();

IList<DineInTable> tablesByGroup = this._dineInTableModel.GetTablesByGroup(tableGroup);
        
            Assert.IsNotNull(tablesByGroup);
            Assert.IsNotNull(tableGroup);
            Assert.IsNotNull(dineInTables);           
          
        }


CanGetDineInTableByGroup : Failed
*** Failures ***
Execute
NHibernate.TransientObjectException: object references an unsaved transient instance - save the transient instance before flushing. Type: Xenon.XenonPOS.DomainModel.Entities.TableGroup, Entity: Xenon.XenonPOS.DomainModel.Entities.TableGroup


Here is what you need to make the test pass:

        [Test]
        private void CanGetDineInTableByGroup()
        {
           
            TableGroup tableGroup = this._dineInTableModel.GetTableGroupById(
                    new Guid("11111111-1111-1111-1111-111111111111"));
var dineInTables = this._dineInTableModel.GetTablesByGroup(tableGroup);

            this._dineInTableModel.CloseConversation();

            this._dineInTableModel.SaveOrUpdateTableGroup(tableGroup);

IList<DineInTable> tablesByGroup = this._dineInTableModel.GetTablesByGroup(tableGroup);
        
            Assert.IsNotNull(tablesByGroup);

            Assert.IsNotNull(tableGroup);
            Assert.IsNotNull(dineInTables);           
          
        }

SaveOrUpdateTableGroup() is actually opening a brand new ISession and attaching detached tableGroup to that session. Rest of the methods is sharing this same new Session.

0 comments:

Post a Comment