First, let me explain the title of this post. The Hibernate folks – you know, that NHibernate knock off written in the Java (pronounced “ex em el”) programming language – have a project called Envers. Among other things, It audits changes to entities, then allows you to easily retrieve the entity as it was at any previous point in time.
Well, Simon Duduica is porting this over to .NET and NHibernate, and he’s making some AMAZING progress. On June 28th, he shared this news with us on the NH Contrib development group:
Hi everybody,
I have news regarding Envers.NET. I’ve commited a version that works in basic tests for CUD operations, with entities that have relationships between them, also with entities that are not audited. To make things work I had to make two small modifications of NHibernate, both modifications were tested running all NHibernate unit tests and they all passed. I already sent the first modification to Fabio and the second I will send this evening. I would like to thank Tuna for helping me out with good advices when I was stuck
![]()
So, on to the topic of this post. For NHibernate 3.0 Cookbook, I’ve included a section that explains how to use NHibernate to generate audit triggers. Originally, I had planned to use the code from my previous blog post on the topic, but I didn’t like its structure. I also didn’t want to include all that plumbing code in the printed book. Instead, I’ve rewritten and contributed the “framework” code to uNHAddIns. The “how-to use it” is explained in the book, so I won’t explain it here.
Today, I was writing an integration test for this contribution, and thought the idea was worth sharing. I have a simple Cat class:
When I do anything to this cat, in addition to the normal INSERT, UPDATE, or DELETE, a database trigger records that action in a table called CatAudit:
I wanted an easy way to investigate the contents of this table to prove that my audit triggers worked. Here’s what I came up with, along with help from Jose Romaniello (@jfroma). First, I created a class to match this table:
Next, I mapped it, made it readonly and excluded it from hbm2ddl with this mapping:
<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2"
assembly="uNhAddIns.Test"
namespace="uNhAddIns.Test.Audit.TriggerGenerator">
<typedef class="NHibernate.Type.EnumStringType`1[[uNhAddIns.Audit.TriggerGenerator.TriggerActions, uNhAddIns]], NHibernate"
name="triggerActions" />
<class name="CatAudit"
mutable="false"
schema-action="none">
<composite-id>
<key-property name="Id" />
<key-property name="AuditUser" />
<key-property name="AuditTimestamp" />
</composite-id>
<property name="Color"/>
<property name="AuditOperation" type="triggerActions" />
</class>
</hibernate-mapping>
I made it readonly by setting mutable="false" and excluded it from hbm2ddl with schema-action="none". That’s it!
By the way, the <typedef> along with type="triggerActions" just tells NHibernate I’ve stored my TriggerActions enum values as strings, not numbers.




Pingback: Tweets that mention NHibernate Auditing v3 – Poor Man’s Envers « Jason Dentler -- Topsy.com
#1 by marek on July 5th, 2010
Hi,
interesting…
I don’t know Envers concept, but I’m afraid that the amount of extra tables (additional audit table per entity) can be disadvantage of the solution.
I think there should be a way to do it using two tables.
#2 by Jason on July 5th, 2010
Thanks for the comment Marek.
Would you store your model in a single table with only 3 columns (EntityId, Key, Value). Why not? It’s not relational.
I’ve seen many systems where there is only one Audit table for everything. They tend to be black holes where information goes in and nothing ever escapes.
The point of this exercise is to make this audit data just as available and accessible as the real application data.
#3 by marek on July 6th, 2010
Hi Jason,
well, yes, the approach I’m talking about is maybe not 100% relational, but, I think it could be tuned to be so.
Please see following solution:
http://www.matthidinger.com/archive/2008/05/08/linq-to-sql-audit-trail.aspx
It is using Linq To Sql but we implemented this idea using NHibernate.
Please note in Audit table TableKey and TableName, using polymorphism you could make relation to your entity.
Thanks.
#4 by Jason on July 6th, 2010
That’s a pretty slick solution, but I don’t see the benefit of saving a few tables over the additional application complexity.