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.
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 ?
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 :
- It exposes a PropertyChangedEventHandler,
- 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
- CastleDynamicProxy page
- Krzysztof Kozmic's tutorial on dynamic proxy
- NHibernate & INotifyPropertyChanged
- Fluent Silverlight - Auto Wiring INotifyPropertyChanged
- Implement InotifyPropertyChanged with Castle.DynamicProxy
- Implementing INotifyPropertyChanged with DynamicProxy2
- Roll Your Own COP. Part I: Mixins
- A library which does nearly the same : Easy prop