Feb 24, 2009

Aspect Oriented Programming & INotifyPropertyChanged

Normally when people talk about aspect oriented programming (AOP) and try and provide a sample they typically talk about logging.  It’s the easiest example for most people to get their head around and it usually involves minimal code.  They might also talk about (but not show) how aspects can be used for security and “so much more”.

Well, since logging is hardly an interesting aspect to talk about and there’s “so much more” out there, I started thinking about what some of the more interesting uses for aspects might be, and my mind turned to something we typically do when implementing binding for WPF applications.

public class WithoutAspects : INotifyPropertyChanged
{
private int myProperty;
public int MyProperty
{
get
{
return myProperty;
}
set
{
if (value != myProperty)
{
myProperty = value;
NotifyPropertyChanged("MyProperty");
}
}
}

public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged(String info)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(info));
}
}
}

The properties in particular bug me.  After all, we now have C# 3.0’s shortcut syntax it would be nice if we could do something like this:

[NotifyPropertyChanged]
public class WithAspects
{
public int MyProperty { get; set; }
}

Here’s where aspects can really help.  Consider the INotifyPropertyChanged implementation and all that binding code that is in our class and think about how it cuts across the purpose of the class itself.  This is exactly what aspects can be used to help with.


If we were to write an aspect that looked for a [NotifyPropertyChanged] attribute on a class and then performed all the work that we are currently hard coding we could then move all that “noise” code out of our class and let the class focus on whatever it is that our class does.


It makes sense, right? So let’s do it.  Let’s write an aspect for INotifyPropertyChanged that does what we want.


First, Which Framework Should We Use?


So the first question that comes to mind is which AOP framework should we use?  Should we use Castle Windsor (or another IoC container), the Policy Injection Application Block or PostSharp?


Windsor and PIAB both have a shortfall that we can’t overcome.  Neither of them can alter the type of a class they are providing aspects for – they simply provide proxies that add hooks to intercept method calls.  We would still need to implement INotifyPropertyChanged in our class for binding to work.


PostSharp on the other hand, applies aspects by altering the compiled code after compilation, meaning that we can create a POCO class and use an aspect to add the INotifyPropertyChanged interface to it.  We’ll go with that for now.


Disclaimer: This Has Already Been Done (Kind Of)


The good news is that there are already examples for doing this out there and in fact a binding aspect is provided as one of the samples in the PostSharp documentation (part of the download).  So, instead of pretending I write all this code from scratch I’ll point you to where I got my starting point


http://thetreeknowseverything.net/2009/01/21/auto-implement-inotifypropertychanged-with-aspects/#


OK, so now that you’re back, did you notice a problem with the implementations?  Neither of them check the existing value of the property to determine if the PropertyChanged(…) method should be fired, they just fire the event every time someone calls the setter.


I don’t want to do that, I want only fire the event when the property value change.  So, let’s run have a run through the code and see how this can be done.


The NotifyPropertyChanged Attribute


So first up we need to decorate our class with an attribute that PostSharp can use to know where to do it’s stuff.


First, we’ll add some references:


image


And then we’ll create the custom attribute.

[Serializable]
[AttributeUsage(AttributeTargets.Assembly | AttributeTargets.Class
, AllowMultiple = false, Inherited = false)]
[MulticastAttributeUsage(MulticastTargets.Class, AllowMultiple = false)]
public class NotifyPropertyChangedAttribute : CompoundAspect
{
public int AspectPriority { get; set; }

public override void ProvideAspects(object element
, LaosReflectionAspectCollection collection)
{
Type type = (Type)element;

collection.AddAspect(type,
new PropertyChangedAspect { AspectPriority = AspectPriority });

foreach (PropertyInfo propertyInfo
in type.GetProperties(BindingFlags.Public | BindingFlags.Instance)
.Where(pi => pi.GetGetMethod() != null && pi.GetSetMethod() != null))
{
collection.AddAspect(propertyInfo.GetSetMethod(),
new NotifyPropertyChangedAspect(propertyInfo.Name
, propertyInfo.PropertyType
, propertyInfo.DeclaringType)
{ AspectPriority = AspectPriority });
}
}
}

Notice that this attribute inherits from the CompondAspect class – this is a PostSharp class used to provide developers with more control over how aspects are applied.  We’re doing this by providing an implementation of the ProvideAspects method.


As you can see, when the ProvideAspects method is called we create a new aspect called PropertyChangedAspect and apply it to the class.  We then run through all the properties on the class, checking that the have both a setter and a getter.  If they do we then create a new NotifyPropertyChangedAspect (written by us) to each of the setters.


What’s in the PropertyChangedAspect and the NotifyChangedAspect? Well, this is where it gets interesting.


The PropertyChangedAspect


This is an aspect we want to apply at the class level.  It’s here that we want to do what it is we need to in order to ensure that the class implements the INotifyPropertyChanged interface,


Now because we’re using aspects we still need to ensure that we have an implementation of INotifyPropertyChanged somewhere in our codebase.  We could get fancy and use Emit to generate something, but there’s no real benefit in doing so.


You’ll also note that the implementation is using a slightly different interface (it still inherits from INotifyPropertyChanged) because we want to be able to call NotifyChanged using a PropertyInfo object as well as using the name of the property.

public interface IPropertyChanged : INotifyPropertyChanged
{
void NotifyChanged(string propertyName);
void NotifyChanged(PropertyInfo property);
}

[Serializable]
internal class PropertyChangedImplementation : IPropertyChanged
{
private readonly object instance;

public PropertyChangedImplementation(object instance)
{
if (instance == null)
throw new ArgumentNullException("instance");

this.instance = instance;
}

public event PropertyChangedEventHandler PropertyChanged;

public void NotifyChanged(string propertyName)
{
if (string.IsNullOrEmpty(propertyName))
throw new ArgumentNullException("propertyName");

PropertyChangedEventHandler eventHandler = PropertyChanged;
if (eventHandler != null)
eventHandler(instance, new PropertyChangedEventArgs(propertyName));
}

public void NotifyChanged(PropertyInfo property)
{
if (property == null)
throw new ArgumentNullException("property");

NotifyChanged(property.Name);
}
}

Here you can see the NotifyChanged implementation looks much the same as in our original class, though our original class actually forgot to check if the passed in string was null.


So now that we have that in place, let’s implement our PropertyChanged aspect.  We can see here that this aspect is a CompositionAspect. The PostSharp docs describe it as follows: The Composition aspect injects new interfaces into an existing type and defers the implementation of these interfaces to another object that implements them.


What that means is that we can combine our PropertyChangedImplementation class with any other class we like, as long as we attach the PropertyChangedAspect to it.  Way cool.


Let’s do just that

[Serializable]
internal class PropertyChangedAspect : CompositionAspect
{
public override object CreateImplementationObject
(InstanceBoundLaosEventArgs eventArgs)
{
return new PropertyChangedImplementation(eventArgs.Instance);
}

public override Type GetPublicInterface(Type containerType)
{
return typeof(IPropertyChanged);
}

public override CompositionAspectOptions GetOptions()
{
return CompositionAspectOptions.GenerateImplementationAccessor;
}
}

OK, so now we’ve accomplished most of what we need.  We’ve taken our class without any INotifyPropertyChanged interface defined for it, and added that interface to it, as well as providing an implementation of that interface in the class itself by composing it with the PropertyChangedImplementation class.


We’re not quite done yet – we still need to implement the changed to the properties themselves so that they fire the event when they change.


The NotifyPropertyChanged Aspect


Remember back in our NotifyPropertyChangedAttribute class that we were attaching the NotifyPropertyChanged aspect to properties in the target class? If you go an look at that code again you’ll see that we have a number of parameters in the constructor we are using.  What are they for?


Let’s have a look:

[Serializable]
internal class NotifyPropertyChangedAspect : OnMethodBoundaryAspect
{
private readonly string propertyName;
private readonly Type propertyType;
private readonly PropertyInfo propertyInfo;

public NotifyPropertyChangedAspect(string propertyName
, Type propertyType, Type classType)
{
if (string.IsNullOrEmpty(propertyName))
throw new ArgumentNullException("propertyName");
if (propertyType == null)
throw new ArgumentNullException("propertyType");
if (classType == null)
throw new ArgumentNullException("classType");

this.propertyName = propertyName;
this.propertyType = propertyType;
propertyInfo = classType.GetProperty(propertyName);
}

So we’re simply storing the property name, the type and getting the reflection PropertyInfo for the property that the aspect is attached to.  Why?  Because we’re going to be reusing it of course :-)  Reflection is slow and we don’t want to do more of it than we need to.


What you might also notice with this class is that it inherits from the OnMethodBoundaryAspect class.  That means that PostSharp will intercept calls to the method and give you the chance to do what you want before making the call.


Lets do just that – we only want to call our property setter when the value of the property has changed.  Since we have an automatic property we need to make that check before the setter gets  executed.  PostSharp lets you do this by providing an OnEntry method.  Let’s implement that method as follows:

public override void OnEntry(MethodExecutionEventArgs eventArgs)
{
var originalValue = Convert.ChangeType(
propertyInfo.GetValue(eventArgs.Instance,null), propertyType);
var newValue = eventArgs.GetReadOnlyArgumentArray()[0];
if ((newValue != null && newValue.Equals(originalValue))
|| (newValue == null && originalValue == null))
{
eventArgs.FlowBehavior = FlowBehavior.Return;
}
}

So, what’s happening here?


First, we grab the current value of the property and ensure it is unboxed (the Convert call).
Next, we get the new value from the eventArgs parameter and check if it’s null
Then we see if the new value is different to the old value. If they’re the same then we set the FlowBehavior to Return so that the actual, real call to the property setter never gets made.


(P.S. If someone knows reflection better than me, they could probably do a better job of the value comparisons.  At least this works)


So now all that’s left is to call the code to fire the event.  PostSharp gives you an OnSuccess method that will get fired when the real method call has completed successfully, so we’ll use that for what we need to do.

public override void OnSuccess(MethodExecutionEventArgs eventArgs)
{
var theObject =
(PropertyChangedImplementation)
((IComposed<IPropertyChanged>)eventArgs.Instance)
.GetImplementation(eventArgs.InstanceCredentials);
theObject.NotifyChanged(propertyName);
}

So, what is going on here now? Well, if you remember that the NotifyChanged code is actually implemented in a different class, we need to be able to get to it.  Without going into the details at this point (you can read through it in the docs) we simply need to get a hold of the object that has the PropertyChangedImplementation for the object we are targeting.  Once we have that, it’s then just a case of calling the NotifyChanged(…) method on it.


Some Obvious? Questions


OK, so now that we have our aspects implemented and we can tag a POCO class with [NotifyPropertyChanged] and get binding working, what does that change in our code.


What happens with the composition of types?


How does that effect our coding.  Unfortunately because PostSharp does IL weaving after compilation you' won’t get Visual Studio knowing that your POCO class has a PropertyChanged event on it.


The following test shows how you have to work with things

[TestMethod]
public void PropertyChangedEventIsFired()
{
var w = new WithAspects();
INotifyPropertyChanged c = w as INotifyPropertyChanged;
c.PropertyChanged += c_PropertyChanged;
eventFired = false;
w.MyProperty = 10;
Assert.IsTrue(eventFired);
eventFired = false;
w.MyProperty = 10;
Assert.IsFalse(eventFired);
w.MyProperty = 12;
Assert.IsTrue(eventFired);
}
We have to cast our object to INotifyPropertyChanged in order to get a handle on the event.  Not perfect, but better than nothing.  Hopefully over time Visual Studio will provide native AOP that

deals with this problem, but I wouldn’t hold my breath :-)


What about the debugging experience?


Debugging still works great.  You can set a breakpoint in your code and step through things like you would expect to.


What about performance?


Aspects slow performance down a fair bit.  I did a test with 200,000 property sets/gets using both the normal method and the AOP method.


The AOP method took 0.265 seconds.  The normal method took 0.003s.  That’s quite a bit slower.


But wait, it’s really not that bad.  That’s 200,000 property changes I’ve done.  And we’re talking about binding – which is a UI related problem.  I don’t think anyone is going to notice that 0.2 second different in their UI if they are changed 200,000 property values in one go.  I’d suspect there are bigger issues at play there if that was happening.


So while performance is slower, in the large scale of things, I doubt there will be a human noticeable difference.


What about the IL?


PostSharp does IL weaving, so what does the IL look like after compilation.  Here’s the property after PostSharp has done it’s thing:


image


You can see where it’s wrapped the setter, and how it’s provided the OnEntry and OnSuccess hooks.  You’ll also note that there are OnException and OnExit hooks if you have situations that need those [think logging :-)]


 


So there you have it, an implementation of INotifyPropertyChanged using Aspect Oriented Programming.  I hope that all made sense to you and that you found it useful.  Comments are welcome, as always, and be aware that I haven’t extensively tested this – it is just sample code, so your mileage may vary. If you do find problems feel free let me know (letting me know the fixes is even more useful).

5 comments:

  1. Richard,
    I am thinking of using some sort of AOP framework for a WCF .Net application.

    How much production experience you have with any of the AOP frameworks?

    Would you recommend the one above?

    Jeff Anderson

    ReplyDelete
  2. While I've used both the Policy Injection Application Block and Castle Windsor in production, I haven't used postsharp in production as yet.

    That said, I think the best thing to do is to figure out what you're wanting to do with the aspects, and then use the one best suited to your needs.

    P.S. If you're using WCF you might want to look at using the WCF dynamic client proxy (http://www.acorns.com.au/blog/?cat=40)

    ReplyDelete
  3. Can you post a sample project please?

    ReplyDelete
  4. Great information thanks for sharing this with us. In fact in all posts of this blog their is something to learn . Your work is very good and I appreciate your work and hopping for some more informative posts . Again thanks

    ReplyDelete
  5. This is i am looking for. That is what I ask valuable. The data provided in here is usually to the most successful. I have to say you will require put in a while in making all these content material jointly. These are match with your theme. I am going to endorse these to any or all and also to each one of my acquaintances. I am going to return here to check out how much give good results. Thank you for building these happen.

    ReplyDelete