I have read many questions and answers, but I couldn´t find solution for my problem. The problem occurs when I migrated from NHibernate 1.2.1 to 2.1.2. I have this common error:
object references an unsaved transient instance - save the transient instance before flushing. Type: Mikro.FareOn.Modules.Cards.Interface.BEC.CRResidenceRegion, Entity: Mikro.FareOn.Modules.Cards.Interface.BEC.CRResidenceRegion
NHibernate configuration: <nhibernate> <add key="hibernate.show_sql" value="true" /> <add key="hibernate.connection.provider" value="NHibernate.Connection.DriverConnectionProvider" /> <add key="dialect" value="NHibernate.Dialect.MsSql2005Dialect" /> <add key="proxyfactory.factory_class" value="NHibernate.ByteCode.Castle.ProxyFactoryFactory, NHibernate.ByteCode.Castle" /> <add key="driver_class" value="NHibernate.Driver.SqlClientDriver" /> <add key="connection.connection_string" value="..." /> </nhibernate>
The mapping of main class CRResidence:
<?xml version="1.0" encoding="utf-8" ?> <hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" default-lazy="false"> <class name="Mikro.FareOn.Modules.Cards.Interface.BEC.CRResidence, Mikro.FareOn.Modules.Cards.Interface" table="CR_Residence"> <id name="Id" column="id" type="Int64" access="nosetter.camelcase" unsaved-value="0"> <generator class="identity" /> </id> <version name="VersionId" column="version_id" type="Int64" /> <property name="IsDeleted" column="deleted" type ="Boolean" not-null="true" /> <property name="Name" column="name" type="String" not-null="true" /> <many-to-one name="Region" column="region_id" cascade="none" class="Mikro.FareOn.Modules.Cards.Interface.BEC.CRResidenceRegion, Mikro.FareOn.Modules.Cards.Interface"/> <property name="Postcode" column="postcode" type="String" not-null="true" /> <many-to-one name="Country" column="residence_country_id" cascade="none" class="Mikro.FareOn.TransportOperator.Interface.BEC.TOResidenceCountry, Mikro.FareOn.TransportOperator.Interface"/> </class> </hibernate-mapping>
And one of the class in relation many-to-one:
<?xml version="1.0" encoding="utf-8" ?> <hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" default-lazy="false"> <class name="Mikro.FareOn.Modules.Cards.Interface.BEC.CRResidenceRegion, Mikro.FareOn.Modules.Cards.Interface" table="CR_ResidenceRegion" > <id name="Id" column="id" type="Int64" access="nosetter.camelcase" unsaved-value="0"> <generator class="identity" /> </id> <version name="VersionId" column="version_id" type="Int64" /> <property name="Name" column="name" type="String" not-null="true" /> <property name="IsDeleted" column="deleted" type ="Boolean" not-null="true" /> </class> </hibernate-mapping>
Implementation of class CRResidenceRegion:
[Serializable] public class CRResidence : BussinesEntityComponentBase //this is class, which implements Equals and define Id, VersionId, IsDeleted { string name; string postcode; CRResidenceRegion region; TOResidenceCountry country; #region Getters/Setters /// /// Nazev mista /// public string Name { get { return this.name; } set { this.name = value; } } /// /// Region /// public CRResidenceRegion Region { get { return this.region; } set { this.region = value; } } /// /// Stát /// public TOResidenceCountry Country { get { return this.country; } set { this.country = value; } } /// /// PSC /// public string Postcode { get { return this.postcode; } set { this.postcode = value; } } public string PostcodeAndName { get { return String.Format("{0} - {1}", Postcode, Name); } } public string NameAndPostcode { get { return String.Format("{1} - {0}", Postcode, Name); } } #endregion /// /// Konstruktor objektu rezidence /// public CRResidence() : base() { } }
I have tried all types of cascades, but any cascade doesn't work correctly. But for my occasion I prefer cascade = "none". I have the client side, where I load the object CRResidenceRegion by calling method on server side in one NHibernate session. After loading the NHibernate session is closed. Object on client is correct, correct Id from database and so on. Then to the CRResidence object is assigned CRResidenceRegion object. Then it is called method on server side:
using (PersistenceSession session = Registrator.OpenSession()) { ITransaction trx = session.BeginTransaction(); try { session.Update(residence); //residence.Region = (CRResidenceRegion)session.Get(typeof(CRResidenceRegion), residence.Region.Id); //residence.Country = (TOResidenceCountry)session.Get(typeof(TOResidenceCountry), residence.Country.Id); trx.Commit(); log.Debug("CRResidence updated."); return residence; } catch (Exception ex) ............ }
I have many objects like this, this problem appered when I migrated to NHibernate 2.1.2. In some occasions works fine after define cascade style ("none" worked as well as "all", where I needed). Could be problem in configuration file of NHibernate.
Previous definition of the problem: When I try to insert or update instance of CRResidence, the error about unsaved transient error occurs. Instance of CRResidenceRegion is completely initialized, including Id, so there is no problem. I have implemented Equal method on Id. I have tried to clear cache, but it wasn't helpful. When I change cascade to save-update, it works, but records of CRResidenceRegion are duplicated. One solution is that I read CRResidenceRegion from database in same session (commented in code below) before I save CRResidence, but this is not acceptable.
It seems that NHibernate has propably lost reference to CRResidenceRegion and thinks that it is new created, but it's not.