Thursday 27 January 2011

Unity Interception And PropertyChanged

Joanna mentioned that on a project that she was working on there was some yet to be specified requirement that whenever a business object changed, it needed to be audited.  Without knowing any more about her specific requirement (or whether it would even ever materialise) I decided to investigate further.
As soon as I heard the vague term "object changed" I immediately thought of IPropertyNotifiedChanged, which is the stock mechanism by which objects notify subscribers that an object has just changed.  This is typically used in data binding where UI classes subscribe to the PropertyChanged event and (business) objects implement the interface in order to raise the event.  The "problem" with this approach is that the object has to implement PropertyChanged() in all its property setters, which is repetitive boiler-plate code and it means our ability to use automatic properties without any implementation evaporates:

public class BoilerplateClass : INotifyPropertyChanged
{
    string _property1;
    string _property2;
    public string Property1
    {
        get { return _property1; }
        set
        {
            _property1 = value;
            PropertyChanged(
                thisnew PropertyChangedEventArgs("Property1"));
        }
    }
 
    public string Property2
    {
        get { return _property2; }
        set
        {
            _property2 = value;
            PropertyChanged(
                thisnew PropertyChangedEventArgs("Property2"));
         }
     }
 
    public event PropertyChangedEventHandler PropertyChanged;
}

Having worked on a number of projects where Dependency Injection had proved useful I'd become quite a fan of Unity and I'd just read Dino Esposito's column in the latest MSDN magazine on the capability of Unity to provide AOP features through its use of interception http://msdn.microsoft.com/en-us/magazine/gg490353.aspx which got me thinking…

Is there a way to use Unity Interception that enables me to use attributes, so that all we need to do for a class to raise a PropertyChanged event is to decorate its property setters (or the class itself) with an attribute so that the class raises the event without the need for boilerplate code as above?

Well, the answer to this rather long and contrived question is "yes, there is!" which is the purpose of this blog posting.
So let's begin…

[Note, here's a big disclaimer: The code is prototypical – it is not necessarily the best, most efficient way to do what I set out to achieve and it suffers from a distinct lack of error checking etc., but hopefully it's useful as a point of reference, if only for me!]

What I did was create a simple Windows Forms application and in the main() method of the static Program class I created an instance of a Unity container: 

using Microsoft.Practices.Unity;
using Microsoft.Practices.Unity.InterceptionExtension;


public static IUnityContainer UnityContainer =
new UnityContainer();


Using the fluent interface we can do this with the following code:

UnityContainer.AddNewExtension<Interception>()
.Configure<Interception>()
.SetDefaultInterceptorFor<SomeEntityClass>
   (new TransparentProxyInterceptor());


We can also achieve the same through configuration, but using the code is fine for demo purposes.

Interception is a class supplied within the Unity.Interception namespace and TransparentProxyInterception is the name of a class from the same namespace that provides runtime interception capabilities. Interception works through a pipeline concept whereby we can hook interceptors into the "pipeline" which connects the caller of a method to the callee.

First we have to create a class to represent the attribute that we're going to decorate our objects with.  This is a very simple class that I've called NotifyPropertyChangedAttribute:

public class NotifyPropertyChangedAttribute : HandlerAttribute
{
   readonly ICallHandler handler;
 
   public NotifyPropertyChangedAttribute()
   {
      handler = new NotifyPropertyChangedCallHandler();
   }
 
   public override ICallHandler CreateHandler(
   IUnityContainer container)
   {
      return handler;
   }
}

The next step is that we have to write the class that we have configured in the UnityContainer to be intercepted:

[NotifyPropertyChanged]
public class SomeEntityClass : MarshallByRefObject, INotifyPropertyChanged
{
    public Guid Identity { getprotected set; }
 
    public event PropertyChangedEventHandler PropertyChanged;

    public void FirePropertyChangedEvent(
    AuditPropertyChangedEventArgs args)
    {
        if (null != PropertyChanged)
        {
            PropertyChanged(this, args);
        }
    }

    public SomeEntityClass()
    {
        Identity = Guid.NewGuid();
        SomeProperty = string.Empty;
    }
 
    public string SomeProperty
    {
        get;
        set;
    }
}

Notice that the class contains a FirePropertyChangedEvent method which simply calls the PropertyChangedEvent.  Also notice that the class is decorated with the [NotifyPropertyChanged] attribute and the class inherits from MarshalByRefObject.  This is a requirement if the class is configured to use the TransparentProxyInterceptor. Finally the FirePropertyChangedEvent method receives a AuditPropertyChangedEventArgs object.  This is just a class that inherits from PropertyChangedEventArgs and it contains the additional information (old value, new value and object identity) needed to audit that the object has changed.  I've left the implementation of that extra information unspecified for now. Here's the very simple implementation of AuditPropertyChangedEventArgs.

public class AuditPropertyChangedEventArgs : PropertyChangedEventArgs
{
    public Guid Identity { getprivate set;  }
    public string Previous { getprotected set; }
    public string Current { getprotected set; }
 
    public AuditPropertyChangedEventArgs(
    string propertyName, Guid identity,
    string previous, string current)
        base(propertyName)
    {
        Identity = identity;
        Previous = previous;
        Current = current;
    }
}

The final and most significant piece in the jigsaw is that we have to create a class that implements ICallHandler, an interface which also lives in the Unity.InterceptionExtension namespace. Implementing this interface means we have to implement the Order property and we also have to implement Invoke, which is the method where we do our core work.  My handler is called NotifyPropertyChangedCallHandler and its Invoke method will be called for every method that is decorated with the attribute above.

The complete class listing is here:

class NotifyPropertyChangedCallHandler : ICallHandler
{
    public int Order
    {
        get
        {
            return 0;
        }
        set { }
    }
    public IMethodReturn Invoke
    (IMethodInvocation input, GetNextHandlerDelegate getNext)
    {
        FireEvent(input);
        IMethodReturn result = getNext.Invoke()
        .Invoke(input, getNext);
        return result;
    }
 
    private void FireEvent(IMethodInvocation input)
    {
        MethodBase method = input.MethodBase;
 
        if (IsProperty(method))
        {
            // Get the method to fire
            MethodInfo mInfo = input.Target.GetType()
            .GetMethod("FirePropertyChangedEvent");
            if (null == mInfo)
            {
                return;
            }
 
            // Get the property name             
            string propertyName = method.Name.Substring(4);
            PropertyInfo property = input.Target.GetType()
            .GetProperty(propertyName);
            if (null == property)
            {
                return;
            }
 
            // Get the property's Get accessor
            MethodInfo getMethod = property.GetGetMethod();
            if (null == getMethod)
            {
                return;
            }
 
            string oldValue = getMethod
            .Invoke(input.Target, null).ToString();
            string newValue = input.Arguments[0].ToString();
 
            // Get the Identity property
            PropertyInfo identityProperty =
            input.Target.GetType().GetProperty("Identity");
            if (null == identityProperty)
            {
               return;
            }
 
            Guid identity = (Guid)identityProperty.
            GetValue(input.Target, null);
            mInfo.Invoke(input.Target, new object[]
            new AuditPropertyChangedEventArgs(
                 propertyName, identity, oldValue, newValue)
            });
        }
    }
 
    private static bool IsProperty(MethodBase method)
    {
        return method.IsSpecialName && \
               method.Name.StartsWith("set_");
    }
}

Hopefully the code comments will suffice, but the following explanation should assist in understanding the purpose of the method.  The first thing Invoke has to do is yield to the next module in the pipeline.
The rest of the code (in the FireEvent method) is rather ugly reflection code which ultimately causes the SomeEntityClass set up in the Unity container to fire a PropertyChanged event.  It does this through the steps outlined below:

  1. Determines that the method that is being invoked is a property (All properties have "backing" "set_" and "get_" methods).
  2. It then gets the Target's FirePropertyChangedEvent method and stores it in mInfo.
  3. It gets the name of the property and stores it in propertyName.
  4. It then gets the "backing" "get_" method behind the property.
  5. Because the "requirement" was to audit changes to an object I use the get method to get the current value of the object's property and store it in oldValue and set the newValue property from the current arguments of the method being invoked. I also grab the Identity of the object (a GUID) and stuff it into a variable called identity.
  6. Finally I invoke the method in mInfo. This causes the FirePropertyChangedEvent method to be called which raises the PropertyChangedEvent event.

So that's nearly it…

Just two questions remain:
Why call the (FirePropertyChangedEvent) method in the intercepted class, rather than calling the event itself?
A: Because there is no reliable way to call an event through reflection. It's much easier to call the method.

But don’t we want to move this interception code into e.g. a layer supertype base class so that all our objects can benefit from this interception?
A: This is a more difficult question to answer – well at least providing the "how" is:

What we would really like to be able to do is this:

public abstract class BaseClass :
MarshalByRefObjectINotifyPropertyChanged
{
    public Guid Identity { getprotected set; }
    public event PropertyChangedEventHandler PropertyChanged;

    public void FirePropertyChangedEvent(
    AuditPropertyChangedEventArgs args)
    {
        if (null != PropertyChanged)
        {
            PropertyChanged(this, args);
        }
    }
}
 
[NotifyPropertyChanged]
public class SomeEntityClass : BaseClass
{
    public SomeEntityClass()
    {
        Identity = Guid.NewGuid();
        SomeProperty = string.Empty;
    }
    public string SomeProperty
    {
        get;
        set;
    }
}

i.e. We would like to be able to split the class into two and refactor the common notify stuff into a base class and simply inherit from it each time we have a derived business object we want to raise PropertyChanged events on.  The difficulty with this approach is there is no out-of-the-box way for us to configure a common base class. We have to supply a single concrete type to the SetDefaultInterceptorFor method and it has to be a class that's going to do the interception, not the name of a common base class. So the following code will not work:

UnityContainer
.AddNewExtension<Interception>()
.Configure<Interception>()
.SetDefaultInterceptorFor<BaseClass>
   (new TransparentProxyInterceptor());
 

This vexed me for a while and I submitted a question on CodePlex.  I was delighted to get an answer back from Unity guru Chris Tavares, who provided me with a suggestion which was just what I was looking for.  Translating his suggestion into code gives the following:

Assembly thisAss = Assembly.GetExecutingAssembly();
foreach (Module mod in thisAss.GetLoadedModules(false))
{
   foreach (Type t in mod.GetTypes())
   {
      if (t.IsSubclassOf(typeof(BaseClass)))
      {
         UnityContainer
         .AddNewExtension<Interception>()
         .Configure<Interception>()
         .SetDefaultInterceptorFor(
         t, new TransparentProxyInterceptor());
      }
   }
}           

So what we do is iterate through all the types in the loaded assembly and add them to Unity interception if they have my BaseClass as an ancestor, rather than just hard-coding the concrete types into the code.

What this now gives us is just what we were looking for. We can simply inherit from our BaseClass, decorate our (automatic) properties or our classes with the [NotifyPropertyChanged] attribute and when code sets the object's properties, we get a PropertyChanged event fired, plus whatever information we might choose to capture to audit that an object has changed.

Now to quickly test the code:

SomeEntityClass sc = UnityContainer.Resolve<SomeEntityClass>();
sc.PropertyChanged +=
new System.ComponentModel.PropertyChangedEventHandler(
m_PropertyChanged);
// Should fire event - class is decorated with [Notify]
sc.SomeProperty = "hello";

static void m_PropertyChanged(
object sender, System.ComponentModel.PropertyChangedEventArgs e)
{
   MessageBox.Show(e.PropertyName);
}

Everything works fine – I get the message box popping up which means my event fired when I set my property.  But hold-on, test the code by writing a method that calls my property and the interception does not work:

[NotifyPropertyChanged]
public class SomeEntityClass : BaseClass
{
    public SomeEntityClass()
    {
        Identity = Guid.NewGuid();
        SomeProperty = string.Empty;
    }
 
    public void SomeMethod()
    {
        SomeProperty = "SomeMethod";
    }
 
    public virtual string SomeProperty
    {
        get;
        set;
    }
}

This is because the TransparentProxyInterceptor is an instance interceptor, which means Unity constructs a proxy object on the fly that sits between my client code and my object.  When I call my property internally through the method on myself, the interception does not work…
Fortunately, there is a different kind of interceptor in Unity called a type interceptor. Unity provides one called the VirtualMethodInterceptor, which performs interception by generating an object that derives from your object at runtime.  For this to work, though, all properties or methods that you want to be intercepted must be marked as virtual. 
For more details on this see the second Dino Esposito article in the series: http://msdn.microsoft.com/en-us/magazine/gg535676.aspx
So if we go back and mark our properties as virtual, remove the inheritance from MarshalByRefObject in our BaseClass (since we no longer require it) and configure Unity to use the VirtualMethodInterceptor as opposed to the TransparentProxyInterceptor the interception works even for properties called from methods from our class.

So there we have it, an interesting way to implement INotifyPropertyChanged using Unity interception and AOP techniques.  What I have presented is not perfect – it seems we either have to inherit from MarshalByRefObject in our (base) entity class if we're using the TransparentProxyInterceptor or all our properties need to be virtual if we're using the VirtualMethodInterceptor.  To set up interception using a common base class requires some reflection code to dynamically load all classes that we want interception to work on, since there is no easy way to do it in the Unity interception API. Also, the implementation of the Invoke method requires us to do some fairly ugly reflection to work out if we should call our PropertyChanged event. Finally, we have to wrap our call to our event in a method call.

No comments:

Post a Comment