Skip to main content

Auditing with nHibernate...

Long time since I've posted anything but I came across an interesting problem the other day whilst I was working - 'How can I audit changes to specific entities when using nHibernate?'

There are several implementation out there already (see Oren's & others posts) but of ones I've seen they are to low level for my liking - they push the auditing of changes into the NH infrastructure away from the Service implementing the business behaviour. I want my service to control and define what is audited I don't want everything audited in the same manner. Auditing for me is the pushing out of events that have affected entities persisted in an OLTP database to OLAP database ideally in an asynchronous manner via some queuing mechanism (MSMQ).

So how do you get events from NH?

Now if you want to observe changes to entities in NH you have to register you're interest by passing an implementation of an IXXXListener interface to the NH configuration when create the SessionFactory, this is the classic implementation of the observer pattern and I guess it's like this because this is a straight port from java - it doesn't follow the .Net approach of EventHandlers & Events, I'm liking this approach as it gives exposure to a different implementation.

What did interest me about this implementation was that as user of the NH Session I can't register with the Session my interest in observing changes to the entities. I have to do this with the SessionFactory and therefore I'm getting notified of ALL events to ALL entities whilst this SessionFactory is alive.

So you can register your interest by implementing a class and passing this to the NH configuration - I've tried to keep it simple by register for events that tell me when CRUD operations have been completed on entities, this is done by implementing the following NH interfaces:

IPostInsertEventListener,
IPostLoadEventListener,
IPostUpdateEventListener,
IPostDeleteEventListener.
I also created an interface called IObserveNHibernate that fires traditional .Net style events when an NH event has been observed.

I've left out all the XXXEventArgs classes from the post for brevity - I hope you can figure out the structure!

public interface IObserveSession<T> : IDisposable where T : IEntity
{
event EventHandler<SessionEventArgs<T>> EntityLoaded;
event EventHandler<SessionEventArgs<T>> EntityCreated;
event EventHandler<SessionEventArgs<T>> EntityUpdated;
event EventHandler<SessionEventArgs<T>> EntityDeleted;
}
So the class looks like:

public sealed class NHibernateObserver : IObserveNHibernate,
IPostInsertEventListener,
IPostLoadEventListener,
IPostUpdateEventListener,
IPostDeleteEventListener
{
public event EventHandler&lt;NHibernateEventArgs&gt; EntityLoaded = delegate { };
public event EventHandler&lt;NHibernateEventArgs&gt; EntityCreated = delegate { };
public event EventHandler&lt;NHibernateEventArgs&gt; EntityUpdated = delegate { };
public event EventHandler&lt;NHibernateEventArgs&gt; EntityDeleted = delegate { };

public void OnPostDelete(PostDeleteEvent @event)
{
EntityDeleted(this, new NHibernateEventArgs(@event.Entity));
}
public void OnPostInsert(PostInsertEvent @event)
{
EntityCreated(this, new NHibernateEventArgs(@event.Entity));
}
public void OnPostLoad(PostLoadEvent @event)
{
EntityLoaded(this, new NHibernateEventArgs(@event.Entity));
}
public void OnPostUpdate(PostUpdateEvent @event)
{
EntityUpdated(this, new NHibernateEventArgs(@event.Entity));
}
}

And this class is then used when creating the SessionFactory for NH. Now currently I wrap the creation of the NH SessionFactory into a custom SessionFactory - this class usually exists as a singleton in the host process and lifetime is managed by the IoC container (it's marked as a singleton).

This SessionFactory is backed by an interface, that defines 2 methods - one for access to the Session & the other for accessing an implementation of SessionObserver<T> exposed as an IObserveSession<T> interface, where the generic T represents the entity type I'm interested in observing.

So the interface looks like:

public interface IObserveSession&lt;T&gt; : IDisposable where T : IEntity
{
event EventHandler&lt;SessionEventArgs&lt;T&gt;&gt; EntityLoaded;
event EventHandler&lt;SessionEventArgs&lt;T&gt;&gt; EntityCreated;
event EventHandler&lt;SessionEventArgs&lt;T&gt;&gt; EntityUpdated;
event EventHandler&lt;SessionEventArgs&lt;T&gt;&gt; EntityDeleted;
}

The interface implements the Dispose pattern so the un-hooking of events automatically happens when you've finished observing NH events. The implementation of the interface is show below:

public sealed class SessionObserver&lt;T&gt; : IObserveSession&lt;T&gt; where T : IEntity
{
public event EventHandler&lt;SessionEventArgs&lt;T&gt;&gt; EntityLoaded = delegate {};
public event EventHandler&lt;SessionEventArgs&lt;T&gt;&gt; EntityCreated = delegate {};
public event EventHandler&lt;SessionEventArgs&lt;T&gt;&gt; EntityUpdated = delegate {};
public event EventHandler&lt;SessionEventArgs&lt;T&gt;&gt; EntityDeleted = delegate {};

private IObserveNHibernate _observer;

public SessionObserver(IObserveNHibernate observer)
{
_observer = observer;
_observer.EntityLoaded += (HandleEntityLoaded);
_observer.EntityCreated += (HandleEntityCreated);
_observer.EntityUpdated += (HandleEntityUpdated);
_observer.EntityDeleted += (HandleEntityDeleted);
}

public void Dispose()
{
_observer.EntityLoaded -= HandleEntityLoaded;
_observer.EntityCreated -= HandleEntityCreated;
_observer.EntityUpdated -= HandleEntityUpdated;
_observer.EntityDeleted -= HandleEntityDeleted;

_observer = null;
EntityCreated = null;
EntityUpdated = null;
EntityDeleted = null;
}

private void HandleEntityDeleted(object sender, NHibernateEventArgs args)
{
if (args.Entity is T)
EntityDeleted(this, new SessionEventArgs&lt;T&gt;((T)args.Entity));
}
private void HandleEntityUpdated(object sender, NHibernateEventArgs args)
{
if (args.Entity is T)
EntityUpdated(this, new SessionEventArgs&lt;T&gt;((T)args.Entity));
}
private void HandleEntityCreated(object sender, NHibernateEventArgs args)
{
if (args.Entity is T)
EntityCreated(this, new SessionEventArgs&lt;T&gt;((T)args.Entity));
}
private void HandleEntityLoaded(object sender, NHibernateEventArgs args)
{
if (args.Entity is T)
EntityLoaded(this, new SessionEventArgs&lt;T&gt;((T)args.Entity));
}
}

So I have a custom SessionFactory that looks something like the following - pretty standard NH Session semantics and the new method SessionObserverT - this creates an instance of the SessionObserver<T> per request.

SessionFactory interface:

public interface IProvideSessions
{
ISession GetSession();
IObserveSession<T> SessionObserver<T>() where T : IEntity;
}
SessionFactory class:

public sealed class SessionFactory : IProvideSessions
{
private readonly ISessionFactory _sessionFactory;
private readonly NHibernateObserver _nHibernateObserver;

public SessionFactory(string connectionString)
{
_nHibernateObserver = new NHibernateObserver();

var cfg = new Configuration();
cfg.EventListeners.PostLoadEventListeners = new IPostLoadEventListener[] { _nHibernateObserver };
cfg.EventListeners.PostInsertEventListeners = new IPostInsertEventListener[] { _nHibernateObserver };
cfg.EventListeners.PostUpdateEventListeners = new IPostUpdateEventListener[] { _nHibernateObserver };
cfg.EventListeners.PostDeleteEventListeners = new IPostDeleteEventListener[] { _nHibernateObserver };

_sessionFactory = Fluently.Configure().ExposeConfiguration(c => cfg.Configure())
.Database(MsSqlConfiguration.MsSql2005.ConnectionString(connectionString).ShowSql())
.Mappings(m => m.FluentMappings.AddFromAssemblyOf<IEntity>())
.BuildSessionFactory();
}

public ISession GetSession()
{
return _sessionFactory.GetCurrentSession() ?? _sessionFactory.OpenSession();
}

public IObserveSession<T> SessionObserver<T>() where T : IEntity
{
return new SessionObserver<T>(_nHibernateObserver);
}
}
So an example of how I would use this is show below, it's a little contrived and not tested but I hope you get the idea:

public class CashService
{
private readonly SessionFactory _sessionFactory = new SessionFactory("SOME CONNECTION STRING");

public void Debit(string accountNumber, decimal amount)
{
using(var session = _sessionFactory.GetSession())
using(var trans = session.BeginTransaction())
using(var sessionObserver = _sessionFactory.SessionObserver<Transaction>())
{
sessionObserver.EntityCreated += ((sender, args) => AuditTransaction(args));

var account = session.Get<Account>(accountNumber);

session.Save(new Transaction(account, amount));

trans.Commit();
}
}

private void AuditTransaction(SessionEventArgs<Transaction> args)
{
// Write transaction event to audit queue...
// if fails the throw exception...
}
}

Now I've not tested this fully and I'm not sure of the viablity of this option or whether I like the end usage - I suppose I could wrap the NH Session behind a custom Session class that exposes the events directly. But it's a start at thinking how I'm going to achieve the auditing required.


Wow that was longer thanexpected ;)

Anyway till next time...


Awkward

Comments

  1. That's interesting.

    I've looked at the implementations that use a separate database call to handle database access from inside an event. It breaks the model a bit, and if (n)Hibernate rolls back, does your audit event also roll back? Not sure if it would be in the same transaction.

    It would be nice if you could access the session from inside an interceptor or event handler.

    I have a case where I have to flush out an object that is loaded from an XML file, and then merged into the session.

    I don't store the primary identifier (identity) in the XML, as the object needs to be portable.

    So I have to do a look-up first to see if I can find the object in the database (there is an alternate key), and set the identifier in the object if found.

    Events and interceptors are a great place to do that, but nHibernate does not seem to want to co-operate.

    ReplyDelete

Post a Comment

Popular posts from this blog

Showing a message box from a ViewModel in MVVM

I was doing a code review with a client last week for a WPF app using MVVM and they asked ' How can I show a message from the ViewModel? '. What follows is how I would (and have) solved the problem in the past. When I hear the words ' show a message... ' I instantly think you mean show a transient modal message box that requires the user input before continuing ' with something else ' - once the user has interacted with the message box it will disappear. The following solution only applies to this scenario. The first solution is the easiest but is very wrong from a separation perspective. It violates the ideas behind the Model-View-Controller pattern because it places View concerns inside the ViewModel - the ViewModel now knows about the type of the View and specifically it knows how to show a message box window: The second approach addresses this concern by introducing the idea of messaging\events between the ViewModel and the View. In the example below

Implementing a busy indicator using a visual overlay in MVVM

This is a technique we use at work to lock the UI whilst some long running process is happening - preventing the user clicking on stuff whilst it's retrieving or rendering data. Now we could have done this by launching a child dialog window but that feels rather out of date and clumsy, we wanted a more modern pattern similar to the way <div> overlays are done on the web. Imagine we have the following simple WPF app and when 'Click' is pressed a busy waiting overlay is shown for the duration entered into the text box. What I'm interested in here is not the actual UI element of the busy indicator but how I go about getting this to show & hide from when using MVVM. The actual UI elements are the standard Busy Indicator coming from the WPF Toolkit : The XAML behind this window is very simple, the important part is the ViewHost. As you can see the ViewHost uses a ContentPresenter element which is bound to the view model, IMainViewModel, it contains 3 child v

Custom AuthorizationHandler for SignalR Hubs

How to implement IAuthorizationRequirement for SignalR in Asp.Net Core v5.0 Been battling this for a couple of days, and eventually ended up raising an issue on Asp.Net Core gitHub  to find the answer. Wanting to do some custom authorization on a SignalR Hub when the client makes a connection (Hub is created) and when an endpoint (Hub method) is called:  I was assuming I could use the same Policy for both class & method attributes, but it ain't so - not because you can't, because you need the signatures to be different. Method implementation has a resource type of HubInnovationContext: I assumed class implementation would have a resource type of HubConnectionContext - client connects etc... This isn't the case, it's infact of type DefaultHttpContext . For me I don't even need that, it can be removed completely  from the inheritence signature and override implementation. Only other thing to note, and this could be a biggy, is the ordering of the statements in th