Here is the next episode of our serie MVVM - Creating ViewModel. A list of all the article about creating a ViewModel is here..

Today we are going to see how to create dynamic proxies for our business objects.

What are Dynamic proxies ?

Readers in a hurry can directly jump to the third part "An implementation".

Proxies are a Desing Pattern which is used a lot in our computer programming world. Here is the Wikipedia definition :

A proxy, in its most general form, is a class functioning as an interface to something else. The proxy could interface to anything: a network connection, a large object in memory, a file, or some other resource that is expensive or impossible to duplicate.


The easiest example to understand I have found is sunglasses. The sunglasses are a proxy between the real world (with all the funky colors) and the world you see trough them (where everything is grey/brown)... You also noticed that the proxy is not neutral, it adds a behaviors(here the gray filter on your vision), but this is not mandatory.
Note : Laurent pointed out that he prefers the TV analogy to explain proxies but I prefer the sunglasses which there is no transformations 3D to 2D.

Proxies

With this in mind let met introduce some vocabulary :

  • The subject: this is the proxied object. In our case : the real world,
  • The client: this is the object which wants to use the subject. In our case : the one who wear the sunglasses,
  • The proxy: this is the object used by the client and which uses the subject. In our case : the sunglasses,
  • The behaviors/interceptors: this is a behavior that the subject does not have and that the proxy creates. In a program, a common interceptor is a logger which tells the start and the end of a method.


Behavior algo example :

public void loggerExampleBehavior(Method theMethod){
  Log("Before method execution.");
  //Execution of the method
  Log("After method execution.");
}


So how to use them for our ViewModel ?(theory)

Readers in a hurry can directly jump to the third part "An implementation".

To be brief : we will add the INotifyPropertyChanged behavior to the business objects by creating a dynamicProxy. We will then no more use directly the business object but the proxies of/to them.
We will so launch the PropertyChangedEvent for each call made to a setter of the business object. Pretty simple ? proxies_INotify_logic.jpg

An implementation with Castle dynamicProxy (code)


To implement the proxy pattern dynamicly we will use Castle Dynamic Proxy. This framework is pretty simple to use as you'll see.

There is only one limitation : properties of your business object must be marked as virtual.

Creation of the proxy

To create our proxy I declare a static method. This method is generic and make any object send NotifyPropertyChanged events on setter calls. The differents class used will be described later.

public class ProxyCreator
{
  public static T MakeINotifyPropertyChanged<T>() where T : class, new()
  {
    //Creates a proxy generator
    ProxyGenerator proxyGen = new ProxyGenerator();
 
    //Generates a proxy using our Interceptor and implementing INotifyPropertyChanged
    var proxy = proxyGen.CreateClassProxy(
      typeof(T),
      new Type[] { typeof(INotifyPropertyChanged) },
      ProxyGenerationOptions.Default,
      new NotifierInterceptor()
      );
 
    return proxy as T;
  }
}

The interceptor


The interceptor does two main things :

  1. It exposes a PropertyChangedEventHandler,
  2. It raises the PropertyChangedEventHandler event when a setter is called with the good name.



Also, I have cached the PropertyChangedEventArgs for better performance.

public class NotifierInterceptor : IInterceptor
{
  private PropertyChangedEventHandler handler;
  public static Dictionary<String, PropertyChangedEventArgs> _cache =
    new Dictionary<string, PropertyChangedEventArgs>();
 
  public void Intercept(IInvocation invocation)
  {
    //Each subscription to the PropertyChangedEventHandler is intercepted (add)
    if (invocation.Method.Name == "add_PropertyChanged")
    {
      handler = (PropertyChangedEventHandler)
            Delegate.Combine(handler, (Delegate)invocation.Arguments[0]);
      invocation.ReturnValue = handler;
    }
    //Each de-subscription to the PropertyChangedEventHandler is intercepted (remove)
    else if (invocation.Method.Name == "remove_PropertyChanged")
    {
      handler = (PropertyChangedEventHandler)
         Delegate.Remove(handler, (Delegate)invocation.Arguments[0]);
      invocation.ReturnValue = handler;
    }
    //Each setter raise a PropertyChanged event
    else if (invocation.Method.Name.StartsWith("set_"))
    {
      //Do the setter execution
      invocation.Proceed();
      //Launch the event after the execution
      if (handler != null)
      {
        PropertyChangedEventArgs arg =
          retrievePropertyChangedArg(invocation.Method.Name);
        handler(invocation.Proxy, arg);
      }
    }
    else invocation.Proceed();
  }
 
  // Caches the PropertyChangedEventArgs
  private PropertyChangedEventArgs retrievePropertyChangedArg(String methodName)
  {
    PropertyChangedEventArgs arg = null;
    NotifierInterceptor._cache.TryGetValue(methodName, out arg);
    if (arg == null)
    {
      arg = new PropertyChangedEventArgs(methodName.Substring(4));
      NotifierInterceptor._cache.Add(methodName, arg);
    }
    return arg;
  }
}

How we use it in the application


We'll only have to expose the proxies to our views with a little snippet of code :

MyBusinessObject myBusinessObject;
DataContext = myBusinessObject = ProxyCreator.MakeINotifyPropertyChanged<MyBusinessObject>();

Interesting links




Shout it kick it on DotNetKicks.com