Sometimes you need to audit everything you do in a transactional system. A lot of standards such as SOX and others state that you must audit your transactions. Doing this by hand inside of your system can (and usually is) a huge PITA. However, it is a necessary evil in some instances.
However, if you’re using Castle’s Active Record (AR) for your database access, then hopefully this post will save you some trouble.
Active Record provides us with the ability to provide interceptors which “intercept” the execution of some of the Active Record calls. I’m using the ActiveRecordMediator<T> class for all of my CRUD operations and in order to implement some sort of auditing I need to be able to intercept all of my Save, Updates and deletes. Here’s the class that does this:
The Interceptor
using System; using System.Collections.Generic; using Castle.ActiveRecord.Framework.Internal; using NHibernate; using NHibernate.Type; namespace Felker.AR.Audit { public class AuditInterceptor : EmptyInterceptor { private static AuditInterceptor instance = new AuditInterceptor(); protected AuditInterceptor() { } public static AuditInterceptor Instance { get { return instance; } set { instance = value; } } public override bool OnFlushDirty(object entity, object id, object[] currentState, object[] previousState, string[] propertyNames, IType[] types) { // Object was updated. Save the current state, previous state, etc. return false; } public override void OnDelete(object entity, object id, object[] state, string[] propertyNames, IType[] types) { // Object was deleted. } public override bool OnSave(object entity, object id, object[] state, string[] propertyNames, IType[] types) { // Object was created. "state" is the state of the object. return false; } } }
The class above implements a default “EmptyInterceptor”. This allows you to override the methods you need in your interceptor. I have overridden the “OnSave”, “OnDelete”, and “OnFlushDirty” methods. When the interceptor is in place, when a Save, Delete or Update call is made with the ActiveRecordMediator<T> the respective overrides will get called.
It is inside of the “OnSave”, “OnDelete”, and “OnFlushDirty” that I can perform my auditing as I am provided with the object state during the save, delete or update. I have not provided any sort of mechanism in the code to actually audit the info to a database or a file as this is up to you to decide “where” it should go. This is only the boiler plate that you can use to intercept the save calls. You have all the data you need. The current entity, the current state (and previous state if it is an update) and the type information.
Why Works So Well
The beauty in this implementation is that I _did_not_ have to change _any_ of my pre-exising ActiveRecordMediator<T> code. I had a TON of models that used a common repository and I did not have to tough them. I just had to add the interceptor to the container (Windsor Container) at app startup and ActiveRecord picked up the interceptor and started firing it.
Wiring up the Interceptor with a Facility
using Castle.ActiveRecord.Framework; using Castle.Core.Configuration; using Castle.MicroKernel; using NHibernate; namespace Felker.Castle.Facilities { ////// Allows for the system to pick up the audit facility which will be used to /// audit all transactions in the system. /// public class AuditFacility : IFacility { #region IFacility Members public void Init(IKernel kernel, IConfiguration facilityConfig) { InterceptorFactory.Create = new InterceptorFactory.CreateInterceptor(CreateAuditInterceptor); } public void Terminate() { // Nothing to terminate } #endregion private IInterceptor CreateAuditInterceptor() { return AuditInterceptor.Instance; } } }
The class above creates a Facility for Active Record. We wire this up in the Global.asax.cs file like so:
static private IWindsorContainer _container; protected void Application_Start(object sender, EventArgs e) { try { _container = new WindsorContainer(Server.MapPath("~/config/windsor.config")); var app = _container.Resolve(); app.RegisterFacilities(_container); app.RegisterComponents(_container); } }
In the Application.cs file we add the facility as such:
public void RegisterFacilities(IWindsorContainer container) { container.AddFacility("audit.support", new AuditFacility()); }
The code above wires up the facility into the container which will handle all of our type registration/etc for us.
Conclusion
The container now contains the facility which will wire up the audit interceptor which will intercept all ActiveRecordMediator<T> calls.
We have not changed a line of code in our existing system, yet we have added the ability to audit all of our CRUD operations in a simple yet effective manner.
1603602377 says
http://www.louisvuittonpurses-bags.net Louis Vuitton Purses
http://www.louisvuittonhandbags-lv.com LV Bags
http://www.cheaplouisvuitton-bags.net Cheap Louis Vuitton
http://www.louisvuitton0.com Louis Vuitton
http://www.louisvuittonoutletsun.net Louis Vuitton Sunglasses
http://www.louisvuittonhandbags-lv.com Louis Vuitton Handbags
http://www.coachsoutletonline.org Coach Online Outlet
http://www.coachfactorysoutletonline.org Coach Factory Online
http://www.coachsfactoryoutlet.net Coach Factory
http://www.coachsoutletstoreonline.com Coach Outlet
http://www.alouisvuittonhandbags.com Authentic Louis Vuitton
http://www.burberryscarfoutlete.net Burberry Outlet
http://www.coachsoutletonline.org Coach Factory Outlet Online
http://www.e-louisvuittonoutlet.net Louis Vuitton Outlet
http://www.burberryscarf-outlet.com Burberry Outlet
http://www.coachfactoryoutletonlinstores.net Coach Factory Outlet Online
http://www.cheapcoachsbags.net Coach Bags
http://www.louisvuittonpurses-bags.net Louis Vuitton Bags
http://www.chanelbags-max.com Chanel Bags
http://www.louisvuittonbagsonline.net Louis Vuitton Bags
http://www.louisvuittonoutletsz.com Louis Vuitton Outlet
http://www.burberrybags-sale.net Burberry Bags
http://www.coachfactorysoutletonline.org Coach Factory Outlet Online
http://www.chanelhandbags-bags.net Chanel Bags
Coach Factory Outlet says
http://buycoachfactoryoutletsz.com