This post details the process to create a domain auditing system using NHibernate events. Upon any change to a mapped entity, details of the change are posted to an audit table in the database.
Setup
- Create the AuditEventListener, which inherits from IPostInsertEventListener, IPostUpdateEventListener and IPostDeleteEventListener; this will enable the logging of inserts, updates and deletes respectively
- A custom time provider is used here, to append the current date and time to the logging, though using the native DateTime.Now is normally sufficient
- Add the OnPostUpdate method and pass in a PostUpdateEvent
- Using the entity property on the PostUpdateEvent, check to ensure the entity is not an AuditLogEntry, as it makes no sense to audit the audit entity
- Verify that the old state of the entity is not null. If the old state is null, it is clear that this is not an update, and so make the method throw an error if this is the case
- Use event.Persister to find the dirty field indexes and get the session
- From the FindDirty method on the Persister property on the PostUpdateEvent, get the dirty field indexes
- Iterate through each of the dirty field indexes. Inside each iteration, once the old and new values are found, check that they differ before inserting an audit log entry into the database
Below are the methods used to get the old and new values, get the event session, and finally insert an audit log entry into the AuditLogEntry table. Note that the AuditLogEntry entity is custom, so a differing range of properties can be logged depending on the needs of the project.
Now to add the code which enables logging for inserts and deletes. These methods are relatively simple in comparison with logging updates and are shown below.
Finally, it is necessary to update the configuration in the NHibernate project to set the listeners, which can be seen below.
Conclusion
What is especially appealing about this method of auditing is it's simplicity. A listener file and a couple of configuration additions is all that's required to track every entity of the application. If any further entities or requests are added to the application there will be no need to change the auditing system in any way.
However, if an application required auditing only for a subset of the entities, it would get a bit more complicated. A custom attribute would have to decorate any entities that were not needed to be audited, then the listener would have to be updated to ensure any changes to these entities were not recorded.
Comments
Post a Comment