Sometimes you do not want to delete the information from the database, but instead to mark it as deleted. This technique is called soft-delete. In this recipe, we will show you how to implement a soft-delete pattern with NHibernate.
SoftDeleteExample
.NHibernate
package using the NuGet Package Manager Console by executing the following command:Install-Package NHibernate
ISoftDeletable
interface using the following code:public interface ISoftDeletable { bool IsDeleted { get; } DateTime? DeletedAt { get; } }
App.config
with a standard NHibernate configuration.sessionfactory
element, add the following three event elements:<event type="delete"> <listener class=" SoftDeleteExample.SoftDeleteEventListener, SoftDeleteExample" /> </event>
EventListener
:public class SoftDeleteEventListener : DefaultDeleteEventListener { protected override void DeleteEntity( IEventSource session, object entity, EntityEntry entityEntry, bool isCascadeDeleteEnabled, IEntityPersister persister, ISet<object> transientEntities) { var deletable = entity as ISoftDeletable; if (deletable != null) { deletable.IsDeleted = true; deletable.DeletedAt = DateTime.UtcNow; CascadeBeforeDelete( session, persister, deletable, entityEntry, transientEntities); CascadeAfterDelete( session, persister, deletable, transientEntities); } else { base.DeleteEntity( session, entity, entityEntry, isCascadeDeleteEnabled, persister, transientEntities); } } }
Order
and OrderLine
classes using the following code:public class Order : ISoftDeletable { public Order() { OrderLines = new HashSet<OrderLine>(); } Guid Id { get; set; } public virtual DateTime OrderDate { get; set; } public virtual ISet<OrderLine> OrderLines { get; set; } public virtual bool IsDeleted { get; set; } public virtual DateTime? DeletedAt { get; set; } }
public class OrderLine : ISoftDeletable { Guid Id { get; set; } public virtual string ProductName { get; set; } public virtual int Amount { get; set; } public virtual bool IsDeleted { get; set; } public virtual DateTime? DeletedAt { get; set; } }
<?xml version="1.0" encoding="utf-8"?> <hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" assembly="SoftDeleteExample" namespace="SoftDeleteExample"> <class name="Order" table="`Order`" where="IsDeleted = 0"> <id name="Id"> <generator class="guid.comb" /> </id> <property name="OrderDate" not-null="true" /> <property name="IsDeleted" not-null="true"/> <property name="DeletedAt" /> <set name="OrderLines" cascade="all-delete-orphan" where="IsDeleted = 0"> <key column="OrderId"/> <one-to-many class="OrderLine"/> </set> </class> <class name="OrderLine" where="IsDeleted = 0"> <id name="Id"> <generator class="guid.comb" /> </id> <property name="ProductName" not-null="true" /> <property name="Amount" not-null="true" /> <property name="IsDeleted" not-null="true"/> <property name="DeletedAt" /> </class> </hibernate-mapping>
The delete event listener works similarly to the event listeners in the Creation and change stamping of entities recipe. The code in the overriden DeleteEntity
method first checks whether the entity implements the ISoftDeletable
interface. If not, the call is just forwarded to the base class for the default execution (that is, the entity will be physically deleted from the system). However, if the entity implements the interface, we set its IsDeleted
property to true
and DeletedAt
to the current UTC time, and call the CascadeBeforeDelete
and CascadeAfterDelete
methods of the base class.
We also use the where
condition on the class
and set
definitions, to restrict loading of the deleted entities.
Instead of using the where
conditions, you can use the filters feature, which is more sophisticated and allows more control over the loading of the entities. You can enable or disable filters as necessary, for example to allow loading deleted entities, if needed.
18.191.62.122