When we wrote NHibernate logical (virtual, 'soft') deletion we showed how to implement virtual deletion of entities (i.e. set IsDeleted = true) by cross-cutting concerns in NHibernate. Since then we've discovered that this raises an issue; when you retrieve a list of entites from NHibernate it doesn't understand that it shouldn't get entites where IsDeleted = true. The initial solution was to add a constraint to our entities (since all our entities subclass BusinessBase we only needed to do this in one place) by adding a Fluent NHibernate Where clause.
public BusinessBaseMap()
{
this.Where("IsDeleted = 0");
this.Id(a => a.Id);
this.Map(a => a.Created);
}Note the string passed to .Where() is SQL.
The constraint did stop deleted entites from being returned but is completely ignored for related collections so you might not get deleted ProductCategories but the Products collection will include deleted Products; not ideal. This caused a little re-think until we realized that this isn't a bug in NHibernate; it isn't the duty of any ORM tool to implement business logic like this - it is the job of the business manager class(es). Needless to say, we left the constraint in place; if NHibernate is going to perform this function for us out-of-the-box we're going to us it!
So back to issue; how to exclude deleted entities from being return in collections? We decided to change our entities collections from 'auto' properties to use backing fields with accessors where the get accessor applies the constraint and NHibernate populates the backing field.
private IList<EmailAddress> emailAddresses = new List<EmailAddress>();
public virtual IList<EmailAddress> EmailAddresses
{
get
{
return this.emailAddresses.Where(a => !a.IsDeleted);
}
}We didn't even need to change our mappings; NHibernate defaults to using a backing field with the name of the property with a lower-case first letter.
ffd0f9e2-d2d5-46a1-8f00-b643eb30a47e|0|.0