Exception when deleting ContentItemVersionRecord with Repository

Topics: Administration, General, Troubleshooting, Writing modules
Mar 2, 2015 at 10:54 PM

I'm attempting to delete (not remove) ContentItemVersions, however attempting a simple Delete on the Repository for the ContentItemVersionRecord throws a strange NHibernate exception which I don't understand:


Dim Repo = Services.WorkContext.Resolve(Of IRepository(Of ContentItemVersionRecord))()


An exception of type 'NHibernate.PropertyAccessException' occurred in NHibernate.dll but was not handled in user code
Exception occurred getter of Orchard.ContentManagement.Records.ContentPartRecord.Id
PersistentType: {Name = "ContentPartRecord" FullName = "Orchard.ContentManagement.Records.ContentPartRecord"}

Inner: {"Object does not match target type."}
at System.Reflection.RuntimeMethodInfo.CheckConsistency(Object target)
at System.Reflection.RuntimeMethodInfo.InvokeArgumentsCheck(Object obj, BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture)
at System.Reflection.RuntimeMethodInfo.Invoke(Object obj, BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture)
at System.Reflection.RuntimePropertyInfo.GetValue(Object obj, BindingFlags invokeAttr, Binder binder, Object[] index, CultureInfo culture)
at System.Reflection.RuntimePropertyInfo.GetValue(Object obj, Object[] index)
at NHibernate.Properties.BasicPropertyAccessor.BasicGetter.Get(Object target)

at NHibernate.Properties.BasicPropertyAccessor.BasicGetter.Get(Object target)
at NHibernate.Tuple.Entity.AbstractEntityTuplizer.GetIdentifier(Object entity)
at NHibernate.Persister.Entity.AbstractEntityPersister.GetIdentifier(Object obj, EntityMode entityMode)
at NHibernate.Persister.Entity.AbstractEntityPersister.IsTransient(Object entity, ISessionImplementor session)
at NHibernate.Engine.ForeignKeys.IsTransient(String entityName, Object entity, Nullable1 assumed, ISessionImplementor session)
at NHibernate.Engine.ForeignKeys.Nullifier.IsNullifiable(String entityName, Object obj)
at NHibernate.Engine.ForeignKeys.Nullifier.NullifyTransientReferences(Object value, IType type)
at NHibernate.Engine.ForeignKeys.Nullifier.NullifyTransientReferences(Object[] values, IType[] types)
at NHibernate.Event.Default.DefaultDeleteEventListener.DeleteEntity(IEventSource session, Object entity, EntityEntry entityEntry, Boolean isCascadeDeleteEnabled, IEntityPersister persister, ISet transientEntities)
at NHibernate.Event.Default.DefaultDeleteEventListener.OnDelete(DeleteEvent event, ISet transientEntities)
at NHibernate.Event.Default.DefaultDeleteEventListener.OnDelete(DeleteEvent event)
at NHibernate.Impl.SessionImpl.FireDelete(DeleteEvent event)
at NHibernate.Impl.SessionImpl.Delete(Object obj)
at Orchard.Data.Repository
1.Delete(T entity)
at Orchard.Data.Repository`1.Orchard.Data.IRepository<T>.Delete(T entity)

Any ideas?

I profiled this using NHprof and it doesn't even get to execute the SQL statement.
Mar 3, 2015 at 10:44 AM
Can you show the code that is setting the versionRecordTodelete variable?
Mar 3, 2015 at 10:54 AM

Dim Results = Services.ContentManager.Query(VersionOptions.AllVersions).ForPart(Of MyPart)().Where(Of MyPart)(Function(x) x.ContentItemRecord.Id = 123).List()

Dim ContentItemToDelete = Results.FirstOrDefault()
If Not ( ContentItemToDelete is Nothing) Then
Dim Repo = Services.WorkContext.Resolve(Of IRepository(Of ContentItemVersionRecord))()
End If
Mar 3, 2015 at 10:40 PM
The only way I've managed to get this working is bypassing the repository pattern and using a Hql instead:
    Dim SessionLocator = Services.WorkContext.Resolve(Of ISessionLocator)()
    Dim Session = SessionLocator.For(GetType(ContentItemVersionRecord))
    Dim Result = Session.CreateQuery(<string>
                                             DELETE FROM Orchard.ContentManagement.Records.ContentItemVersionRecord
                                             WHERE Id = :id
                                         </string>).SetParameter("id", CurrentVersion.Id).ExecuteUpdate()
Where CurrentVersion.Id is the Id of the ContentItemVersionRecord you wish to delete.

I would be interested to know why the repo method doesn't work.
Mar 4, 2015 at 2:15 AM
Maybe because you use .Where(Of MyPart) in place of .Where(Of MyPartRecord)

Mar 4, 2015 at 8:56 AM
@jtkech - Sorry was a typo on my end, the actual code I have is:

ForPart(Of MyPart)().Where(Of MyPartRecord)

I am going to try a simple:

Dim Item = Repo.Get(id)

Later tonight and see whether it succeeds.
Mar 4, 2015 at 9:58 PM
I could repro, and it's the same with the last test that you want to do. I think it's because of the mapping override on the ContentItemVersionRecord entity, see in ContentItemAlteration.cs. So, you have to deal directly with the underlying session...

But, why do you want to alter directly the ContentItemVersionRecord table? There are so many relations between ContentItem, ContentPart, ContentItemRecord, ContentItemVersionRecord...

Mar 5, 2015 at 8:50 AM
@jtkech - Okay I will take a look at the ContentItemAlteration class code tonight to see what it's doing.

I'm writing an extension method which will deal with Deleting any content item from Orchard, plus firing the appropriate OnDeleted method on any Handlers.

In my opinion this is one area of Orchard which really sucks. I completely understand the argument of "you need to have history just in case you need to restore data", but to NOT have the ability to delete content items from the system is mad. If the author(s) of the project are so anal about keeping data then remove the Delete ability from the IRepository interface and be done with it.

If the pattern exists then I should be able to use it without issue.

Again, if there are relationships between entities (as you say ContentItem -> Versions -> Parts) then they should be enforced. Orchard doesn't do ANY of this out of the box e.g. I can attach a Part to a Content Item, delete the version and the parts still remain - there is no integrity being maintained.

Perhaps I'm in the minority here, but if I want to store 1M+ content items with versioning and parts - and want to archive the data - how is this done? Simply "hiding" the version doesn't seem a good solution to me.
Mar 5, 2015 at 3:17 PM
Oh, I see. So, in the Orchard 1.x source code, take a look on the Orchard.AuditTrail module (RecycleBin.cs, RecycleBinController.cs...) that particularly uses the new new Destroy() public method of content manager, see in DefaultContentManager.cs

Mar 5, 2015 at 9:51 PM
wow - the Destroy method that has been implement in 1.x is pretty much identical to what I had to do in order to get this to work. I wonder if they ran into the same problem I did :)