Add a listener to the pre-event and do your custom logic implementing either one of IPreDeleteEventListener
, IPreInsertEventListener
, IPreUpdateEventListener
within the NHibernate.Event
namespace.
A neat example by Ayende Rahien: NHibernate IPreUpdateEventListener & IPreInsertEventListener.
public class AuditEventListener : IPreInsertEventListener, IPreUpdateEventListener {
public bool OnPreInsert(OnPreInsert @event) {
var audit = @event.Entity as IHaveAuditInformation;
if (audit == null) return false;
var time = DateTime.Now;
var name = WindowsIdentity.GetCurrent().Name;
Set(@event.Persister, @event.State, "CreatedAt", time);
Set(@event.Persister, @event.State, "CreatedBy", name);
audit.CreatedAt = time;
audit.CreatedBy = name;
return false;
}
public bool OnPreUpdate(OnPreUpdate @event) {
var audit = @event.Entity as IHaveAuditInformation;
if (audit == null) return false;
var time = DateTime.Now;
var name = WindowsIndentity.GetCurrent().Name;
Set(@event.Persister, @event.State, "UpdatedAt", time);
Set(@event.Persister, @event.State, "UpdatedBy", name);
audit.UpdatedAt= time;
audit.UpdatedBy = name;
return false;
}
}
The same can be done with the IPreDeleteEventListener
.
Notice the return value false
. This should actually be one of the two OnPreEventResult
enum values.
OnPreEventResult.Continue
(false)OnPreEventResult.Break
(true)
As per the answer from @Radim Köhler to this question:
So, since the enum doesn't exist, instead of return true
or false
, I prefered to return the boolean value through another method call which actually says what it does explicitely.
private bool AbortOperation() { return true; }
private bool ContinueOperation() { return false; }
And replacing the return false
by return ContinueOperation()
. This makes the code clearer and reveals the exact intention and behaviour of the pre-event methods.
After the interfaces are implemented, just add the listener to the configuration.
var listener = new AuditEventListener();
Configuration cfg = new Configuration();
c.SetListener(ListenerType.PreDelete, listener);
c.SetListener(ListenerType.PreInsert, listener);
c.SetListener(ListenerType.PreUpdate, listener);
Now, all remain to do is a clean call to ISession.SaveOrUpdate()
, having used the cascade="all"
mapping attribute, and you're done!