Sponsored By Aspose - File Format APIs for .NET

Aspose are the market leader of .NET APIs for file business formats – natively work with DOCX, XLSX, PPT, PDF, MSG, MPP, images formats and many more!

Using nHibernate EventListeners to validate and audit data

In an application you would like to have maximum control over interaction with the database. In the ideal situation there is one single point where all data can be checked and monitored before it is sent to, or read from the database. In nHibernate EventListeners are an ideal way to do that. Every entity read from or sent to the db, whether explicitly flushed or part of a graph, passes there. nHibernate has a long list of Events you can listen to. At first sight the documentation on picking the right listeners and how to implement them points to an article by Ayende. Alas there are some severe issues taking that direction. There is a better way.

The problem

Entities in our domain can have quite complex validation. My base DomainObject class has a string-list of possible Issues, and a Validate method. An object with an empty issue list is considered valid. What makes an object invalid is described in the issue-list. Implementing this validation in the nHibernate OnPreUpdate event listener would seem a solid way to trap all validation errors.

Code Snippet
  1. public bool OnPreUpdate(PreUpdateEvent preUpdateEvent)
  2. {
  3.     var domainObject = preUpdateEvent.Entity as DomainObject;
  4.     if (domainObject == null)
  5.         return false;
  6.     domainObject.Validate();
  7.     if (domainObject.Issues.Any())
  8.         throw new InvalidDomainObjectException(domainObject);
  9.     return false;
  10. }

 

Pretty straigthforward. The Validate method performs the validation. In case this results in any issues an exception is thrown and the update is canceled. But there is a huge problem with this approach. As the validation code can, and will, do almost anything there is a chance it will touch a lazy collection. Resulting in an nHibernate exception. It is performing a flush, the lazy collection will trigger a read. This results in the dreaded “Collection was not processed in flush” exception.

The way out

There are loads and loads of events your code can listen to. The OnFlushEntity event is fired before the OnPreUpdate event. It fires at the right occasion and the best part is that you can touch anything while inside.

Code Snippet
  1. public void OnFlushEntity(FlushEntityEvent flushEntityEvent)
  2. {
  3.     if (HasDirtyProperties(flushEntityEvent))
  4.     {
  5.         var domainObject = flushEntityEvent.Entity as DomainObject;
  6.         if (domainObject != null)
  7.             domainObject.Validate();
  8.         }
  9.     }
  10. }

 

The validation is only performed, I leave the rejection of invalid entities in the OnPreUpdate event.

Code Snippet
  1. public bool OnPreUpdate(PreUpdateEvent preUpdateEvent)
  2. {
  3.     var domainObject = preUpdateEvent.Entity as DomainObject;
  4.     if (domainObject == null)
  5.         return false;
  6.     if (domainObject.Issues.Any())
  7.         throw new InvalidDomainObjectException(domainObject);
  8.     return false;
  9. }

 

Crucial in the OnFlushEntity event is the HasDirtyProperties method. This method was found here, in a GitHub contribution by Filip Kinsky, just about the only documentation on the event.

Code Snippet
  1. private bool HasDirtyProperties(FlushEntityEvent flushEntityEvent)
  2. {
  3.     ISessionImplementor session = flushEntityEvent.Session;
  4.     EntityEntry entry = flushEntityEvent.EntityEntry;
  5.     var entity = flushEntityEvent.Entity;
  6.     if (!entry.RequiresDirtyCheck(entity) || !entry.ExistsInDatabase || entry.LoadedState == null)
  7.     {
  8.         return false;
  9.     }
  10.     IEntityPersister persister = entry.Persister;
  11.  
  12.     object[] currentState = persister.GetPropertyValues(entity, session.EntityMode);
  13.     object[] loadedState = entry.LoadedState;
  14.  
  15.     return persister.EntityMetamodel.Properties
  16.         .Where((property, i) => !LazyPropertyInitializer.UnfetchedProperty.Equals(currentState[i]) && property.Type.IsDirty(loadedState[i], currentState[i], session))
  17.         .Any();
  18. }

 

As stated, you can do almost anything in the OnFlushEntity event, modifying data in entities included. So this is an ideal place to set auditing properties, or even add items to collections of the entity. All these modifications will be persisted to the database.

The original post by Ayende was on setting auditing properties, not about validation. Modifying an entity inside the OnPreUpdate event can be done, but takes some fiddling. Having discovered OnFlushEntity we moved not only the validation but also the auditing code here.

Setting eventhandlers

So far I have described the event handlers but have not shown yet how to hook them up. The eventhandlers are defined in interfaces, to be implemented in a class. The snippets above are all members of one class. 

Code Snippet
  1. public class RechtenValidatieEnLogListener : IPreUpdateEventListener, IPreInsertEventListener, IPreDeleteEventListener, IPostLoadEventListener, IFlushEntityEventListener
  2. {
  3.  
  4.   // ……
  5.  
  6. }

 

Used  when creating the sessionfactory.

Code Snippet
  1. private static ISessionFactory GetFactory()
  2. {
  3.     var listener = new RechtenValidatieEnLogListener();
  4.     return Fluently.Configure().
  5.         Database(CurrentConfiguration).
  6.         Mappings(m => m.FluentMappings.AddFromAssembly(Assembly.GetExecutingAssembly())).
  7.         ExposeConfiguration(c => listener.Register(c)).
  8.         CurrentSessionContext<HybridWebSessionContext>().
  9.         BuildSessionFactory();
  10. }

 

Registering the handlers is done by the Register method, which uses the configuration

Code Snippet
  1. public void Register(Configuration cfg)
  2. {
  3.      cfg.EventListeners.FlushEntityEventListeners = new[] { this }
  4.         .Concat(cfg.EventListeners.FlushEntityEventListeners)
  5.         .ToArray();
  6.      cfg.EventListeners.PreUpdateEventListeners = new[] { this }
  7.         .Concat(cfg.EventListeners.PreUpdateEventListeners)
  8.         .ToArray();
  9.  }

 

Most examples on hooking in handlers use the cfg.SetListener method. The problem with that is that it knocks out any handlers already hooked in. For the OnPreUpdate event that’s no problem, but knocking out the default OnFlushEntity event is fatal. Using this code your custom listener will be combined with any handlers already set.

Winding down

That’s all there is to it. I have left out the parts on validating reads, inserts or deletes. They follow the same pattern, up to you to implement them. EventListeners are very powerful, but it is a pity the documentation is so sparse. All of this was found by scraping the web and a lot of trial and error. But now we have a very solid system for validation and auditing. Without any limitations.

This entry was posted in Uncategorized. Bookmark the permalink. Follow any comments here with the RSS feed for this post.
  • PeterGekko

    Still a interesting discussion. You sum up our differences quite well. To me the limits of what’s possible in the DM is what I can talk about with my domain user. I can communicate in terms of an invalid customer object, not in terms of a set of distributed rules spread across the model. It’s not just ubiquitous language, also an ubiquitous structure.

  • http://enterprisecraftsmanship.com/ Vladimir Khorikov

    Sorry for the late answer and thank you for this discussion, I really enjoy it.

    I guess I could sum up the differences in our approaches with the following. You tend to keep all the validation rules in the domain objects themselves. I have to admit that’s a nice way to handle them as the rules don’t get spread across the whole domain model. The downside of it is that you can’t leverage the benefits of design by contract as your domain entities can remain in an invalid state.

    My approach, on the other hand, chooses the opposite side. I like having an ability to work with objects that are always valid. At the same time, my approach does tend to spread the validation rules across the domain model.

    It would be great to somehow combine these approaches together to get the best from the both worlds, but it seems like it’s not possible, unfortunately.

  • PeterGekko

    Quite an interesting discussion. We do agree the validation has to be part of the DM.
    The DM is not just a central part of the code, it is also essential in the conversation with the (domain-) user. Up till now now the discussion has focused on “our” side of the DM as developers. For the domain user a DM is collection of recognisable entities. With recognisable properties and behaviours. The domain user can describe validation rules on the entities. “The address of the customer has to be known.” Or “an order is acceptable in case it contains at least 10 items, or the customer has paid a previous order within 10 days”. In our domain there are loads and loads of silly validation rules which are often incomprehensible but quite important because they are (FI) dictated by legislation. Summing up the rules in the DO’s and checking them one by one comes closest to the domain users version of the story. Moving the rules into a separate part of the model has the risk of losing or malforming things in translation. And the model will become too abstract for the domain users. And then the, IMHO, core of DDD is lost.
    By accepting a DO has state, which can be an invalid state, I do have a strong communication tool. I can communicate with the user what has to be done to get things done the way desired. “Please enter an address”. “This customer has not paid his last bill”. “Govt. rule #2345As78 prohibits the use of this medicine for patients under 18”. All of this communication is described in the DM.
    The knowledge a DO can have an invalid state is used by the repositories. They validate the DO before persisting, as described in the post. This can also be easily communicated with the domain user. A DO which does not obey the validation rules will not make it to the DB. Close your browser and it’s gone.
    I do see benefits of your style for myself. Coding against contracts and having a DO which is always valid is nice. A factory is quite nice. Our repositories do something in that direction, they perform a (partial) validation of the DO when reading. But as I see it your desire can lead to immutable objects. What about a DO which is handled by a complex method, which changes properties one by one, where the new values are dependent on the value of a property changed in an earlier step. (An external service not being the best example). During the proces the DO will be invalid. At the end the result can be validated. And persisted. Or discarded.
    As said, I think I have to jump through some hoops coding this style. And it will make communication with my users harder.

  • http://enterprisecraftsmanship.com/ Vladimir Khorikov

    Thank you for your reply.

    I agree that the main difference between our approaches is the assumption about domain objects’ validity; all other just grow from this one. You provide valid points here, but I still think DOs can be made valid during all their lifespan. I’ll try to clarify my point of view.

    1) First of all, if you allow DOs to enter an invalid state, you lose all the benefits that coding by contract approach introduces ( http://en.wikipedia.org/wiki/Object-Oriented_Software_Construction ). That is, contracts not only allow us to validate data flowing between domain objects, they also help build a knowledge base regarding our domain. It’s a declarative way to include domain knowledge in our code. With your approach, it’s impossible to use them.

    2) It’s much easier to follow the program flow and write programs in general if you know that an object you are working with is always valid. Without it, you need to validate it every time you get such object in order to avoid other DOs entering an inconsistent state.

    3) The technique for always keeping objects in a valid state is rather simple. For each DO with some complex validation rules, you can create a factory method returning a result containing the resulting object or an error in case creation failed:

    Result<User> userResult = _userFactory.Create(model.Name, model.Email);

    Scott Wlaschin discusses this approach in detail here: http://fsharpforfunandprofit.com/rop/ . Also, I wrote on similar topic here: http://enterprisecraftsmanship.com/2015/03/07/functional-c-primitive-obsession/

    Source code for the Result class can be found here (it’s just my implementation of it): https://github.com/vkhorikov/DddInAction/blob/ef115ed9d2cdb28b88f6029064c00cbf5edc6153/DddInAction.Logic/Common/Result.cs

    You write “what about a DO waiting for an external service”. In my opinion, DOs shouldn’t wait for external services at all, it’s just not theirs area of responsibility. DOs should represent domain; external services should be dealt with by your application layer, i.e. controllers for a web app or presenters/VM for a desktop app. So, my answer to this question is – don’t create a DO if you don’t have all the data needed to create a fully valid DO.

    4) I agree that validation logic is a part of DM, but I disagree that it should belong to DOs. This point flows from the general idea of DOs always remaining in a valid state.

  • PeterGekko

    Nice article, good points.

    My approach does not put the validation logic itself into the db layer. The listeners only has the following knowledge of the DO’s it is trying to persist: The state can be checked by counting the issues. No issues => valid. The DO can be validated by invoking the Validate method. Whatever happens in there is unknown to the listener. Only the result, no issues, counts.
    Both Issues and Validate are member of the DomainObject base class, just like the Id.
    This is not by definition the only point where validation is performed. But it does assure that only valid DO’s are persisted to the DB. The same listener technique could be used to prevent invalid DO’s entering your code when reading from the DB
    The main difference between your approach and mine is that in my case a domain object can be in an invalid state. Your quote : “If an invalid data is inside your domain model, it is simply too late to do anything”. The problem is how to prevent DO’s entering an invalid state. Coding errors or bad user input can be dealt with, but what about a DO waiting for an external service.. While waiting it could be very invalid.. You do have a point in the desire to have only a valid DO’s. But I do not see a practical way to have a single point to guard that.

    To sum up your three points
    Validation is a part of the DM. Used by the persistence layer, without any claim on being the single point of validation It would be nice to have DO’s which are always valid. But I do not see a way to keep them always valid. I do present a way to guard invalid DO’s against persistence. The listeners’s assure the data being stored in the DB will be valid. It could assume data in the DB was invalid, FI being entered via another route. In such a scenario a listener could prevent invalid DO’s entering your code.

  • http://enterprisecraftsmanship.com/ Vladimir Khorikov

    Nice example of using NH event listeners, but I disagree with the hole concept of putting validation logic there. Please take a look at my article: http://enterprisecraftsmanship.com/2015/04/27/validation-logic-and-nhibernate-event-listeners/