C# – Using dynamic proxy on NHibernate objects

ccastle-dynamicproxydynamic-proxynhibernatePROXY

I'm trying to use Castle.DynamicProxy2 to cleanup code within NHibernate persisted classes. Here is a simple version of it.

The Pet class:

public class Pet
{
    public int Id { get; set; }
    public string Name { get; set; }
    public int Age { get; set; }
}

And its mapping file:

<class name="Pet" table="Pet">
  <id name="Id" column="Id" unsaved-value="0">
    <generator class="native"/>
  </id>
  <property name="Name" column="Name"/>
  <property name="Age" column="Age"/>
</class>

There is a need to audit instances of the Pet class. Normally, the properties Name and Age would not be auto-properties and would contain logic to record value changes. Now, I'm thinking of using proxies to inject auditing logic within property setters. To do that, I created the Auditor IInterceptor:

public class Auditor : IInterceptor
{
    public void Intercept(IInvocation invocation)
    {
        // Do something to record changes
        invocation.Proceed();
    }
}

It's simple enough to create an audited instance of the Pet class using Castle.DynamicProxy2.

Pet aPet = proxyGenerator.CreateClassProxy<Pet>(new Auditor());
aPet.Name = "Muffles"; // The Auditor instance would record this.
aPet.Age = 4;          // and this too...

Now here comes the problem. Since Pet is persisted, the system would need to work on instances of Pet fetched via NHibernate. What I want to happen is that NHibernate to return instances of Pet proxy automatically like so:

// I would imagine an instance of Auditor being created implicitly
ICriteria criteria = session.CreateCriteria(typeof(Pet));
criteria.Add(Expression.Expression.Eq("Name", "Muffles"));

// return a list of Pet proxies instead
// so changes can be recorded.
IList<Pet> result = criteria.List<Pet>();

Pet aPet = result[0];
aPet.Age = aPet.Age + 1;

// I expect this to succeed since the proxy is still a Pet instance
session.Save(aPet); 

I've though of something like this to get around it:

ICriteria criteria = session.CreateCriteria(ProxyHelper.GetProxyType<Pet>());
criteria.Add(Expression.Expression.Eq("Name", "Muffles"));

// use List() instead of List<T>()
// and IList instead of IList<Pet>
IList results = criteria.List();

where ProxyHelper.GetProxyType<Pet>() would return the cached Pet proxy type. The main disadvantage is that this solution would not work on generic lists (e.g. IList<Pet>). The existing system I'm trying to clean up makes use of them extensively.

So I'm hoping if anyone has any workaround or any insight on whether or not what I'm doing is advisable.

Thanks a million,

Carlos

Best Answer

You could use NHibernate Event Listeners.

These hook into NHibernate's event system and intercept such actions. This may even be a better idea than using proxies, due to the obvious run-time creation-of-proxy performance gains.

The link actually shows an Auditing app example.