Search code examples
c#nhibernatefluent-nhibernateorchardcms

NHibernate OrchardCMS and Long Id: object references an unsaved transient instance


I'm using OrchardCMS (1.10.x) as a Web Application. OrchardCMS use integer field as primary key on database (ContentItemRecord.Id). However, soon, the web application will need more than pass the max value for int32.MaxValue (2.147.483.647). In order to not break the production environment, we need that OrchardCMS support long value instead of int32.

I already start doing the job and refactor all application to work with long values. However, in some operations I'm getting the following error:

NHibernate.TransientObjectException: object references an unsaved transient instance - save the transient instance before flushing or set cascade action for the property to something that would make it autosave. 
Type: App.Core.Models.XPTOPartRecord, Entity: App.Core.Models.XPTOPartRecord
   at NHibernate.Engine.ForeignKeys.GetEntityIdentifierIfNotUnsaved(String entityName, Object entity, ISessionImplementor session)
   at NHibernate.Type.ManyToOneType.IsDirty(Object old, Object current, Boolean[] checkable, ISessionImplementor session)
   at NHibernate.Type.TypeHelper.FindDirty(StandardProperty[] properties, Object[] currentState, Object[] previousState, Boolean[][] includeColumns, Boolean anyUninitializedProperties, ISessionImplementor session)
   at NHibernate.Persister.Entity.AbstractEntityPersister.FindDirty(Object[] currentState, Object[] previousState, Object entity, ISessionImplementor session)
   at NHibernate.Event.Default.DefaultFlushEntityEventListener.DirtyCheck(FlushEntityEvent event)
   at NHibernate.Event.Default.DefaultFlushEntityEventListener.IsUpdateNecessary(FlushEntityEvent event, Boolean mightBeDirty)
   at NHibernate.Event.Default.DefaultFlushEntityEventListener.OnFlushEntity(FlushEntityEvent event)
   at NHibernate.Event.Default.AbstractFlushingEventListener.FlushEntities(FlushEvent event)
   at NHibernate.Event.Default.AbstractFlushingEventListener.FlushEverythingToExecutions(FlushEvent event)
   at NHibernate.Event.Default.DefaultAutoFlushEventListener.OnAutoFlush(AutoFlushEvent event)
   at NHibernate.Impl.SessionImpl.AutoFlushIfRequired(ISet`1 querySpaces)
   at NHibernate.Impl.SessionImpl.List(CriteriaImpl criteria, IList results)
   at NHibernate.Impl.CriteriaImpl.List(IList results)
   at NHibernate.Impl.CriteriaImpl.List[T]()
   at Orchard.ContentManagement.DefaultContentQuery.Slice(Int32 skip, Int32 count) in C:\App\src\Orchard\ContentManagement\DefaultContentQuery.cs:line 191
   at Orchard.ContentManagement.DefaultContentQuery.ContentQuery`1.Orchard.ContentManagement.IContentQuery<T>.List() in C:\App\src\Orchard\ContentManagement\DefaultContentQuery.cs:line 292
   at App.Core.Services.Service.GetData(string val)
   at App.Core.Services.Service.Create(List`1 obj)
   at App.Api.Controllers.Create(List`1 obj)

My Object is declared this way (the id is from OrchardCMS) and I don't have any specific configuration on NHibernate.

// From OrchardCMS   
public abstract class ContentPartRecord 
{
        public virtual long Id { get; set; }
        
        [CascadeAllDeleteOrphan]
        public virtual ContentItemRecord ContentItemRecord { get; set; }
}
    
public abstract class BasePartRecord : ContentPartRecord
{
        public virtual string UUID { get; set; }
        public virtual DateTime? Created { get; set; }
        public virtual string Name { get; set; }
        
        // Some other props
}

public class XPTOPartRecord : BasePartRecord
{
    public XPTOPartRecord()
    {
    
    }
    
    public virtual bool Flag { get; set; }    
    public virtual string Status { get; set; }
    public virtual XPTOPartRecord ParentXPTOPartRecord { get; set; }
    
    // Some other props (not referenced props)
}

This only happens after change de Id from INT32 to LONG. In order to troubleshooting this problem, I activate the NHibernate logs and track what's happening inside NHibernate. The majority of the logs are equal, except this:

-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
INT VALUES
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
2023-11-08 21:15:44,715 [22] NHibernate.Event.Default.AbstractFlushingEventListener - Default - Flushing entities and processing referenced collections
2023-11-08 21:15:44,718 [22] NHibernate.Engine.Collections - Default - Collection found: [Orchard.ContentManagement.Records.ContentItemRecord.Versions#17314], was: [Orchard.ContentManagement.Records.ContentItemRecord.Versions#17314] (uninitialized)
2023-11-08 21:15:44,721 [22] NHibernate.Engine.Collections - Default - Collection found: [Orchard.ContentManagement.Records.ContentItemRecord.Versions#18544], was: [Orchard.ContentManagement.Records.ContentItemRecord.Versions#18544] (uninitialized)
2023-11-08 21:15:44,723 [22] NHibernate.Engine.Collections - Default - Collection found: [Orchard.ContentManagement.Records.ContentItemRecord.Versions#2011389097], was: [Orchard.ContentManagement.Records.ContentItemRecord.Versions#2011389097] (initialized)
2023-11-08 21:15:44,724 [22] NHibernate.Persister.Entity.AbstractEntityPersister - Default - Orchard.ContentManagement.Records.ContentItemVersionRecord.Published is dirty
2023-11-08 21:15:44,726 [22] NHibernate.Persister.Entity.AbstractEntityPersister - Default - Orchard.ContentManagement.Records.ContentItemVersionRecord.Latest is dirty
2023-11-08 21:15:44,727 [22] NHibernate.Event.Default.DefaultFlushEntityEventListener - Default - Updating entity: [Orchard.ContentManagement.Records.ContentItemVersionRecord#2011389098]
2023-11-08 21:15:44,729 [22] NHibernate.Engine.Collections - Default - Collection found: [App.Core.Models.ControlPartRecord.Entries#2011389097], was: [App.Core.Models.ControlPartRecord.Entries#2011389097] (initialized)
2023-11-08 21:15:44,718 [22] NHibernate.Engine.Collections - Default - Collection found: [Orchard.ContentManagement.Records.ContentItemRecord.Versions#17314], was: [Orchard.ContentManagement.Records.ContentItemRecord.Versions#17314] (uninitialized)
2023-11-08 21:15:44,721 [22] NHibernate.Engine.Collections - Default - Collection found: [Orchard.ContentManagement.Records.ContentItemRecord.Versions#18544], was: [Orchard.ContentManagement.Records.ContentItemRecord.Versions#18544] (uninitialized)
2023-11-08 21:15:44,723 [22] NHibernate.Engine.Collections - Default - Collection found: [Orchard.ContentManagement.Records.ContentItemRecord.Versions#2011389097], was: [Orchard.ContentManagement.Records.ContentItemRecord.Versions#2011389097] (initialized)
2023-11-08 21:15:44,724 [22] NHibernate.Persister.Entity.AbstractEntityPersister - Default - Orchard.ContentManagement.Records.ContentItemVersionRecord.Published is dirty
2023-11-08 21:15:44,726 [22] NHibernate.Persister.Entity.AbstractEntityPersister - Default - Orchard.ContentManagement.Records.ContentItemVersionRecord.Latest is dirty
2023-11-08 21:15:44,727 [22] NHibernate.Event.Default.DefaultFlushEntityEventListener - Default - Updating entity: [Orchard.ContentManagement.Records.ContentItemVersionRecord#2011389098]
2023-11-08 21:15:44,729 [22] NHibernate.Engine.Collections - Default - Collection found: [App.Core.Models.ControlPartRecord.Entries#2011389097], was: [App.Core.Models.ControlPartRecord.Entries#2011389097] (initialized)
2023-11-08 21:15:44,730 [22] NHibernate.Persister.Entity.AbstractEntityPersister - Default - App.Core.Models.XPTOPartRecord.Prop1 is dirty // <-- here
2023-11-08 21:15:44,731 [22] NHibernate.Persister.Entity.AbstractEntityPersister - Default - App.Core.Models.XPTOPartRecord.Prop2 is dirty // <-- here
2023-11-08 21:15:44,733 [22] NHibernate.Persister.Entity.AbstractEntityPersister - Default - App.Core.Models.XPTOPartRecord.Prop3 is dirty // <-- here
2023-11-08 21:15:44,735 [22] NHibernate.Persister.Entity.AbstractEntityPersister - Default - App.Core.Models.XPTOPartRecord.Prop4 is dirty // <-- here
2023-11-08 21:15:44,738 [22] NHibernate.Persister.Entity.AbstractEntityPersister - Default - App.Core.Models.XPTOPartRecord.Prop5 is dirty // <-- here
2023-11-08 21:15:44,740 [22] NHibernate.Persister.Entity.AbstractEntityPersister - Default - App.Core.Models.XPTOPartRecord.Prop6 is dirty // <-- here
2023-11-08 21:15:44,741 [22] NHibernate.Persister.Entity.AbstractEntityPersister - Default - App.Core.Models.XPTOPartRecord.Prop7 is dirty // <-- here
2023-11-08 21:15:44,742 [22] NHibernate.Persister.Entity.AbstractEntityPersister - Default - App.Core.Models.XPTOPartRecord.Prop8 is dirty // <-- here
2023-11-08 21:15:44,743 [22] NHibernate.Persister.Entity.AbstractEntityPersister - Default - App.Core.Models.XPTOPartRecord.Prop9 is dirty // <-- here
2023-11-08 21:15:44,745 [22] NHibernate.Persister.Entity.AbstractEntityPersister - Default - App.Core.Models.XPTOPartRecord.Prop10 is dirty // <-- here
2023-11-08 21:15:44,746 [22] NHibernate.Event.Default.DefaultFlushEntityEventListener - Default - Updating entity: [App.Core.Models.XPTOPartRecord#2011389097] // <-- here
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
LONG VALUES
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
2023-11-08 21:05:47,970 [49] NHibernate.Event.Default.AbstractFlushingEventListener - Default - Flushing entities and processing referenced collections
2023-11-08 21:05:47,972 [49] NHibernate.Engine.Collections - Default - Collection found: [Orchard.ContentManagement.Records.ContentItemRecord.Versions#17314], was: [Orchard.ContentManagement.Records.ContentItemRecord.Versions#17314] (uninitialized)
2023-11-08 21:05:47,975 [49] NHibernate.Engine.Collections - Default - Collection found: [Orchard.ContentManagement.Records.ContentItemRecord.Versions#18544], was: [Orchard.ContentManagement.Records.ContentItemRecord.Versions#18544] (uninitialized)
2023-11-08 21:05:47,978 [49] NHibernate.Engine.Collections - Default - Collection found: [Orchard.ContentManagement.Records.ContentItemRecord.Versions#2011389093], was: [Orchard.ContentManagement.Records.ContentItemRecord.Versions#2011389093] (initialized)
2023-11-08 21:05:47,980 [49] NHibernate.Persister.Entity.AbstractEntityPersister - Default - Orchard.ContentManagement.Records.ContentItemVersionRecord.Published is dirty
2023-11-08 21:05:47,981 [49] NHibernate.Persister.Entity.AbstractEntityPersister - Default - Orchard.ContentManagement.Records.ContentItemVersionRecord.Latest is dirty
2023-11-08 21:05:47,983 [49] NHibernate.Event.Default.DefaultFlushEntityEventListener - Default - Updating entity: [Orchard.ContentManagement.Records.ContentItemVersionRecord#2011389094]
2023-11-08 21:05:47,984 [49] NHibernate.Engine.Collections - Default - Collection found: [App.Core.Models.ControlPartRecord.Entries#2011389093], was: [App.Core.Models.ControlPartRecord.Entries#2011389093] (initialized)
2023-11-08 21:05:47,987 [49] NHibernate.Engine.IdentifierValue - Default - unsaved-value: 0 // <-- here
NHibernate.TransientObjectException: object references an unsaved transient instance - save the transient instance before flushing or set cascade action for the property to something that would make it autosave. 
Type: App.Core.Models.XPTOPartRecord, Entity: App.Core.Models.XPTOPartRecord
   at NHibernate.Engine.ForeignKeys.GetEntityIdentifierIfNotUnsaved(String entityName, Object entity, ISessionImplementor session)
   at NHibernate.Type.ManyToOneType.IsDirty(Object old, Object current, Boolean[] checkable, ISessionImplementor session)
   at NHibernate.Type.TypeHelper.FindDirty(StandardProperty[] properties, Object[] currentState, Object[] previousState, Boolean[][] includeColumns, Boolean anyUninitializedProperties, ISessionImplementor session)
   at NHibernate.Persister.Entity.AbstractEntityPersister.FindDirty(Object[] currentState, Object[] previousState, Object entity, ISessionImplementor session)
   at NHibernate.Event.Default.DefaultFlushEntityEventListener.DirtyCheck(FlushEntityEvent event)
   at NHibernate.Event.Default.DefaultFlushEntityEventListener.IsUpdateNecessary(FlushEntityEvent event, Boolean mightBeDirty)
   at NHibernate.Event.Default.DefaultFlushEntityEventListener.OnFlushEntity(FlushEntityEvent event)
   at NHibernate.Event.Default.AbstractFlushingEventListener.FlushEntities(FlushEvent event)
   at NHibernate.Event.Default.AbstractFlushingEventListener.FlushEverythingToExecutions(FlushEvent event)
   at NHibernate.Event.Default.DefaultAutoFlushEventListener.OnAutoFlush(AutoFlushEvent event)
   at NHibernate.Impl.SessionImpl.AutoFlushIfRequired(ISet`1 querySpaces)
   at NHibernate.Impl.SessionImpl.List(CriteriaImpl criteria, IList results)
   at NHibernate.Impl.CriteriaImpl.List(IList results)
   at NHibernate.Impl.CriteriaImpl.List[T]()
   at Orchard.ContentManagement.DefaultContentQuery.Slice(Int32 skip, Int32 count) in C:\App\src\Orchard\ContentManagement\DefaultContentQuery.cs:line 191
   at Orchard.ContentManagement.DefaultContentQuery.ContentQuery`1.Orchard.ContentManagement.IContentQuery<T>.List() in C:\App\src\Orchard\ContentManagement\DefaultContentQuery.cs:line 292
   at App.Core.Services.Service.GetData(string val)
   at App.Core.Services.Service.Create(List`1 obj)
   at App.Api.Controllers.Create(List`1 obj)
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

Versions: OrchardCMS: 1.10.x NHibernate: 4.0.0.4000 Fluent Nhibernate: 1.4.0

Someone knows any tip how can I fix this issue? Or I can troubleshooting better why this is happening? Thanks in advance.

SOLUTION

After digging into NHibernate and debug throught it, I find out the problem on my solution. In specific methods of my application, I initialize the ParentXPTOPartRecord.Id with 0 value. When I execute the flush, NHibernate detect that reference does not exist and throw an TransientObjectException.

After fix the wrong initialization of the reference (ParentXPTOPartRecord = null) all works. So the fix was fix my reference iniatilization in specific methods of my application.


Solution

  • Most probably there is an XPTOPartRecord object which references an unsaved XPTOPartRecord object through property ParentXPTOPartRecord. Either save the new object before saving the object referencing it or add [CascadeSaveUpdate] to property ParentXPTOPartRecord.