Implementing an Interceptor Using NHibernate’s Built In Dynamic Proxy Generator

Posted by Ricardo Peres on ASP.net Weblogs See other posts from ASP.net Weblogs or by Ricardo Peres
Published on Tue, 19 Jun 2012 11:28:17 GMT Indexed on 2012/06/19 15:17 UTC
Read the original article Hit count: 472

Filed under:
|
|

NHibernate 3.2 came with an included proxy generator, which means there is no longer the need – or the possibility, for that matter – to choose Castle DynamicProxy, LinFu or Spring. This is actually a good thing, because it means one less assembly to deploy. Apparently, this generator was based, at least partially, on LinFu.

As there are not many tutorials out there demonstrating it’s usage, here’s one, for demonstrating one of the most requested features: implementing INotifyPropertyChanged. This interceptor, of course, will still feature all of NHibernate’s functionalities that you are used to, such as lazy loading, and such.

We will start by implementing an NHibernate interceptor, by inheriting from the base class NHibernate.EmptyInterceptor. This class does not do anything by itself, but it allows us to plug in behavior by overriding some of its methods, in this case, Instantiate:

   1: public class NotifyPropertyChangedInterceptor : EmptyInterceptor
   2: {
   3:     private ISession session = null;
   4:  
   5:     private static readonly ProxyFactory factory = new ProxyFactory();
   6:  
   7:     public override void SetSession(ISession session)
   8:     {
   9:         this.session = session;
  10:         base.SetSession(session);
  11:     }
  12:  
  13:     public override Object Instantiate(String clazz, EntityMode entityMode, Object id)
  14:     {
  15:         Type entityType = Type.GetType(clazz);
  16:         IProxy proxy = factory.CreateProxy(entityType, new _NotifyPropertyChangedInterceptor(), typeof(INotifyPropertyChanged)) as IProxy;
  17:         
  18:         _NotifyPropertyChangedInterceptor interceptor = proxy.Interceptor as _NotifyPropertyChangedInterceptor;
  19:         interceptor.Proxy = this.session.SessionFactory.GetClassMetadata(entityType).Instantiate(id, entityMode);
  20:  
  21:         this.session.SessionFactory.GetClassMetadata(entityType).SetIdentifier(proxy, id, entityMode);
  22:  
  23:         return (proxy);
  24:     }
  25: }

Then we need a class that implements the NHibernate dynamic proxy behavior, let’s place it inside our interceptor, because it will only need to be used there:

   1: class _NotifyPropertyChangedInterceptor : NHibernate.Proxy.DynamicProxy.IInterceptor
   2: {
   3:     private PropertyChangedEventHandler changed = delegate { };
   4:  
   5:     public Object Proxy
   6:     {
   7:         get;
   8:         set;}
   9:  
  10:     #region IInterceptor Members
  11:  
  12:     public Object Intercept(InvocationInfo info)
  13:     {
  14:         Boolean isSetter = info.TargetMethod.Name.StartsWith("set_") == true;
  15:         Object result = null;
  16:  
  17:         if (info.TargetMethod.Name == "add_PropertyChanged")
  18:         {
  19:             PropertyChangedEventHandler propertyChangedEventHandler = info.Arguments[0] as PropertyChangedEventHandler;
  20:             this.changed += propertyChangedEventHandler;
  21:         }
  22:         else if (info.TargetMethod.Name == "remove_PropertyChanged")
  23:         {
  24:             PropertyChangedEventHandler propertyChangedEventHandler = info.Arguments[0] as PropertyChangedEventHandler;
  25:             this.changed -= propertyChangedEventHandler;
  26:         }
  27:         else
  28:         {
  29:             result = info.TargetMethod.Invoke(this.Proxy, info.Arguments);
  30:         }
  31:  
  32:         if (isSetter == true)
  33:         {
  34:             String propertyName = info.TargetMethod.Name.Substring("set_".Length);
  35:             this.changed(this.Proxy, new PropertyChangedEventArgs(propertyName));
  36:         }
  37:  
  38:         return (result);
  39:     }
  40:  
  41:     #endregion
  42: }

What this does for every interceptable method (those who are either virtual or from the INotifyPropertyChanged) is:

  • For methods that came from the INotifyPropertyChanged interface, add_PropertyChanged and remove_PropertyChanged (yes, events are methods Winking smile), we add an implementation that adds or removes the event handlers to the delegate which we declared as changed;
  • For all the others, we direct them to the place where they are actually implemented, which is the Proxy field;
  • If the call is setting a property, it fires afterwards the PropertyChanged event.

In order to use this, we need to add the interceptor to the Configuration before building the ISessionFactory:

   1: using (ISessionFactory factory = cfg.SetInterceptor(new NotifyPropertyChangedInterceptor()).BuildSessionFactory())
   2: {
   3:     using (ISession session = factory.OpenSession())
   4:     using (ITransaction tx = session.BeginTransaction())
   5:     {
   6:         Customer customer = session.Get<Customer>(100);    //some id
   7:         INotifyPropertyChanged inpc = customer as INotifyPropertyChanged;
   8:         inpc.PropertyChanged += delegate(Object sender, PropertyChangedEventArgs e)
   9:         {
  10:             //fired when a property changes
  11:         };
  12:         customer.Address = "some other address";    //will raise PropertyChanged
  13:         customer.RecentOrders.ToList();            //will trigger the lazy loading
  14:     }
  15: }

Any problems, questions, do drop me a line!

© ASP.net Weblogs or respective owner

Related posts about .NET

Related posts about nhibernate