Hard delete Content Items?

Topics: Customizing Orchard, General, Troubleshooting, Writing modules
Aug 24, 2011 at 6:16 PM

Hi everybody,

What options exist for permanently deleting Content Items from the database?

I see there's a module that deletes Content Types but not items that have been created from the type.

What is the purpose of Repository's Delete()? Does it do the same thing as ContentManager's Remove()?

Coordinator
Aug 24, 2011 at 7:21 PM

Orchard only does soft deletes, which is what the Content Manager's remove is doing. You would have to act at the repository level, using Delete, to do a hard delete (it does Delete on the nHibernate session). The tricky part is that you need to delete all parts connected to that content item as the tables are not physically linked and there is no cascading in place.

Aug 24, 2011 at 8:53 PM

Awesome! Merci beaucoup. That's exactly what I was looking for with the Repository.

One small thing: after I deleted all parts associated with the content item, I queried the ContentManager to see if it can still get a count of items...

articles_saved = _contentManager.Query(VersionOptions.AllVersions, "Article").Count();

And it still returns the number of items that existed before the Repository deletion.

Could the ContentManager be picking up on something left over? Is there anything else apart from the Content Parts that I need to delete to clear out the item fully?

Aug 24, 2011 at 9:01 PM

Here's a method that I wrote that does hard deletes.  It is pretty nasty as you have mentioned, but I think it's as clean as possible.  Maybe someone with some real NHibernate knowledge could let me know if I'm doing anything poorly.

This method takes the ID of your content item and delete the IdentityPartRecord, ContentItemRecord and all ContentItemVersionRecord rows.  So in order to use this method, you need to do what Bertrand said first and delete YOUR content part, (which we handle in the OnRemoved of the Part's Handler) and then we call this method to really clean everything up.

Let me know if there is a better place to post this code...

public void CleanupRelatedContentItemRecords(int contentItemId)
{
	ISessionLocator sessionLocator = this.orchardServices.WorkContext.Resolve<ISessionLocator>();

	// Delete the IdentityPartRecord
	ISession identityPartRecordRecordSession = sessionLocator.For(typeof(IdentityPartRecord));

	string hSql = string.Format("delete from {0} where id = {1}", typeof(IdentityPartRecord).FullName, contentItemId.ToString());

	IQuery query = identityPartRecordRecordSession.CreateQuery(hSql);

	query.ExecuteUpdate();

	identityPartRecordRecordSession.Flush();

	// Delete the ContentItemRecord
	ISession contentItemRecordSession = sessionLocator.For(typeof(ContentItemRecord));
			
	hSql = string.Format("delete from {0} where id = {1}", typeof(ContentItemRecord).FullName, contentItemId.ToString());

	query = identityPartRecordRecordSession.CreateQuery(hSql);

	query.ExecuteUpdate();

	contentItemRecordSession.Flush();

	// Delete ContentItemVersionRecord's
	ISession contentItemVersionRecordSession = sessionLocator.For(typeof(ContentItemVersionRecord));

	hSql = string.Format("delete from {0} where ContentItemRecord_id = {1}", typeof(ContentItemVersionRecord).FullName, contentItemId.ToString());

	query = identityPartRecordRecordSession.CreateQuery(hSql);

	query.ExecuteUpdate();

	contentItemVersionRecordSession.Flush();
}

Hope this helps,

Vinnie

Coordinator
Aug 24, 2011 at 9:32 PM

You would need to delete both the parts and the content item.

Aug 24, 2011 at 10:57 PM

Thanks vrossi, I see that I was missing the three you mentioned (IdentityPartRecord, ContentItemRecord, and ContentItemVersionRecord)

Bertrand, I'm having trouble deleting the Content Item in the same manner as the parts...

private readonly IRepository<ContentItemRecord> _repository_contentitem;
...
List<ContentItem> articles = _contentManager.Query(VersionOptions.AllVersions, "Article").List().ToList<ContentItem>();
...
 _repository_contentitem.Delete(articles[i].Record);
 _repository_contentitem.Flush();

It errors out on the .Delete() line..says that "Object does not match target type."

I'm almost there! Thanks for your help.

Coordinator
Aug 24, 2011 at 11:07 PM

Yes, if you inspect that record object in a debugger, it may not be your content item record. Did you try to query the content item repo directly instead of CM?

Aug 25, 2011 at 7:01 AM

I just tried the query with the repo instead and got the same result. And yes they are the same records I see in the database.

I thought maybe it had something to do with deleting the parts first so I tried to delete the content item first and then parts, but that didn't help.

Thinking it might be an access issue, I just grabbed the content item record IDs, released the List<ContentItemRecord>, and tried _repository.Delete(_repository.Get(ID)), no difference again.

I'm putting some of the trace here for reference:

 

[TargetException: Object does not match target type.]
   System.Reflection.RuntimeMethodInfo.CheckConsistency(Object target) +12768673
   System.Reflection.RuntimeMethodInfo.Invoke(Object obj, BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture, Boolean skipVisibilityChecks) +222
   System.Reflection.RuntimeMethodInfo.Invoke(Object obj, BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture) +38
   System.Reflection.RuntimePropertyInfo.GetValue(Object obj, Object[] index) +37
   NHibernate.Properties.BasicGetter.Get(Object target) +84

[PropertyAccessException: Exception occurred getter of Orchard.ContentManagement.Records.ContentPartRecord.Id]
   NHibernate.Properties.BasicGetter.Get(Object target) +217
   NHibernate.Tuple.Entity.AbstractEntityTuplizer.GetIdentifier(Object entity) +255
   NHibernate.Persister.Entity.AbstractEntityPersister.IsTransient(Object entity, ISessionImplementor session) +110
   NHibernate.Engine.ForeignKeys.IsTransient(String entityName, Object entity, Nullable`1 assumed, ISessionImplementor session) +180
   NHibernate.Engine.Nullifier.IsNullifiable(String entityName, Object obj) +223
   NHibernate.Engine.Nullifier.NullifyTransientReferences(Object value, IType type) +129
   NHibernate.Engine.Nullifier.NullifyTransientReferences(Object[] values, IType[] types) +66
   NHibernate.Event.Default.DefaultDeleteEventListener.DeleteEntity(IEventSource session, Object entity, EntityEntry entityEntry, Boolean isCascadeDeleteEnabled, IEntityPersister persister, ISet transientEntities) +712
   NHibernate.Event.Default.DefaultDeleteEventListener.OnDelete(DeleteEvent event, ISet transientEntities) +1109
   NHibernate.Impl.SessionImpl.FireDelete(DeleteEvent event) +395
   NHibernate.Impl.SessionImpl.Delete(Object obj) +267

Any thoughts based on the trace?
Coordinator
Aug 25, 2011 at 8:30 PM

You mean they are content item records, not content part records?

Aug 25, 2011 at 10:40 PM

Yes. I was able to successfully delete the different Content Parts (records) of the ContentType, and you said I needed to also delete the ContentItem iteself ..which I assume is also a record.

Have I mixed something up?

Coordinator
Aug 25, 2011 at 11:32 PM

It looks as if the default repository implementation is somehow expecting a part record, at least in the mapping. You may want to try skipping the repository and acting directly through the nHibernate Session (which is what the default content manager is doing).

Developer
Jul 23, 2012 at 8:03 PM

Any updates on this one? The issue with removing version records is still here...

Oct 16, 2013 at 12:24 PM
Someone has already solved this problem?

I think this is a problem with mapping alteration (in Orchard.ContentManagement.Records.ContentItemAlteration class from Orchard.Framework project).
Is created new properies In Alteration<TItemRecord, TPartRecord> class. Nhibernate can not process this properies on Delete method.
I would like to check it out using the source code nhibernate, but when I download it from github, project generated an error, that AssemblyInfo.cs doesnt exists. (How i can get source code of nhibernate, which used by orchard?)