We are using NHibernate on our project and we are hooking into the pre update/insert/delete events to do some auditing.
We want to audit each entity to its own audit table that has the same schema as the source table (maybe with an audit operation such as "Update", "Insert" etc).
I have looked at the unHAddins that will generate triggers automatically but it seems to drop and recreate the audit tables when you make a change to the main table which we cannot accept, we also need to have some custom logic such that we will only audit the record under certain circumstances of its actual properties (the properties we care about are part of the base class for all of our objects).
To do this simply I figured that I can just extend our existing domain classes and then define a new Nhibernate mapping for those extended classes.
For example:
We have a class Instrument
public class Instrument : BaseObject, IAuditable
{
public virtual string Title { get; set; }
public virtual IList<Control> Controls { get; set; }
public virtual CouncilRegion Region { get; set; }
public Type GetAuditType()
{
return typeof (InstrumentAudit);
}
I have defined an interface Iauditable so that we can find out what class is the auditing class for any Iauditable object.
The auditing class is as follows
public class InstrumentAudit : Instrument
{}
It has no additional functionality in it it is basically a hack to let us map it to our audit table in NHibernate.
So this seems like it would work but the problem actually comes in when trying to handle the NHibernate events
public class EventListener: IPreInsertEventListener, IPreUpdateEventListener, IPreDeleteEventListener
{
private readonly IAuditLogger _logger = new AuditLogger();
public bool OnPreInsert(PreInsertEvent e)
{
Audit(e.Entity as BaseObject, AuditType.Insert);
return false;
}
}
private void Audit(object entity, AuditType auditType)
{
if(entity is IAuditable && entity is BaseObject)
{
_logger.Log(entity, auditType);
}
}
The e.Entity is given to me as an Object.
public class AuditLogger : IAuditLogger
{
public void Log(object entity, AuditType auditType)
{
if (entity is IAuditable && entity is BaseObject)
{
var auditObject = entity as IAuditable;
Type type = auditObject.GetAuditType();
var x = (type) auditObject;
DataRepository.Instance.Save(x);
}
}
}
Above is the code I would like to work, basically I know that the object is one that should be audited and it is one of my base objects, so I would like to convert it to the subtype of the audit object and pass it to nhibernate to save.
Unfortunately it doesn't seem like you can cast to a variable it has to be an actual type, is there anyway around this so I can cast or convert the object to its "audit" type without having to put constructors/convertors for each audit type that takes its base type and saves the properties?
well, the easy answer is, you can't if your auditObject is no instance of InstrumentAudit or a subclass
just because there are no additional fields or methods between the Instrument and InstrumentAudit classes, it doesn't mean you can easily cast from Instrument to InstrumentAudit because Instrument is no subclass of InstrumentAudit
if you want a way to clone your objects without implementing x converters, have a look @ expression trees: you could setup a dictionary of functions, indexed by the corresponding audit types, that stores "clone" functions ... if you find a clone function for the needed type in the dict, use it ... if not, create one using expression trees, and store it for later use