Quantcast
Channel: Development With A Dot
Viewing all articles
Browse latest Browse all 404

Lesser-Known NHibernate Features: Proxy Generation

$
0
0

Did you know that you can leverage NHibernate’s built-in proxy generator to inject custom behavior in your classes? It is called NHibernate.Proxy.DynamicProxy.ProxyFactory and there’s an interface, NHibernate.Proxy.DynamicProxy.IInterceptor, that you can use to extend it, by intercepting method and property calls.

First, you inject an NHibernate interceptor, inheriting from EmptyInterceptor, in the configuration instance, before building the session factory:

   1:publicsealedclass NotifyPropertyChangedInterceptor : EmptyInterceptor
   2: {
   3:privatestaticreadonly ProxyFactory factory = new ProxyFactory();
   4:  
   5:private ISession session = null;
   6:  
   7:publicoverridevoid SetSession(ISession session)
   8:     {
   9:this.session = session;
  10:base.SetSession(session);
  11:     }
  12:  
  13:publicoverride Object Instantiate(String clazz, EntityMode entityMode, Object id)
  14:     {
  15:         var entityType = this.session.SessionFactory.GetClassMetadata(clazz).GetMappedClass(entityMode);
  16:         var target = this.session.SessionFactory.GetClassMetadata(entityType).Instantiate(id, entityMode);
  17:         var proxy = factory.CreateProxy(entityType, new _NotifyPropertyChangedInterceptor(target), typeof(INotifyPropertyChanged));
  18:  
  19:this.session.SessionFactory.GetClassMetadata(entityType).SetIdentifier(proxy, id, entityMode);
  20:  
  21:return (proxy);
  22:     }
  23: }

The code in the Instantiate method will add the INotifyPropertyChanged interface to the generated proxy instance, a common interface that is used for detecting property changes.

But the real fun is in the NHibernate.Proxy.DynamicProxy.IInterceptor implementation:

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

This will detect calls to properties (starting with set_) and, after they complete, will raise the PropertyChanged event. This will save you lots of lines in your code!

You just need to register the NHibernate interceptor, and your done:

   1: cfg.SetInterceptor(new NotifyPropertyChangedInterceptor());

And from now one, all your entities automagically implement INotifyPropertyChanged:

   1: var product = session.Get<Product>(1);
   2: var npc = product as INotifyProductChanged;
   3: npc.PropertyChanged += (s, e) => 
   4: {
   5:if (e.PropertyName == "Price")
   6:     {
   7:         Console.WriteLine("Someone changed the price of the product!");
   8:     }
   9: };






Viewing all articles
Browse latest Browse all 404

Trending Articles



<script src="https://jsc.adskeeper.com/r/s/rssing.com.1596347.js" async> </script>