views:

466

answers:

2

Hi,

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

A: 

Have a look at this post. Author uses notion of ProxyFactoryFactory (nice name =)) in the NHibernate to implement custom proxies for entity classes.

it is possible to set ProxyFactoryFactory type through configuration or at code.

Maybe this will solve your task.

elder_george
I checked out the post and after looking up ProxyFactoryFactory, I stumbled on http://ayende.com/Blog/archive/2007/04/17/Advance-Extending-NHibernate-Proxies.aspx. Although extending NHibernate to use my own proxy factory is great, it would only work on lazy loaded classes. I still need to proxy classes which are not lazy loaded.
ca7l0s
+3  A: 

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.

_NT
+1 Auditing seems to be an added responsibility for the entity class.
Aaron Fischer