Showing posts with label AOP. Show all posts
Showing posts with label AOP. Show all posts

Monday, August 11, 2014

What about Catel.Fody and computed read-only properties change notifications?


In my last post, I covered the implementation of INotifyPropertyChanged interface when using SheepAspect as an AOP library. In the end, I also implemented an approach to notify changes of computed read-only properties. This approach has a downside, the dependent properties discovering process must be done in run-time.

Such a journey recalls us that Catel.Fody didn’t have support for notifying property changes of computed read-only properties. How could such a thing ever be possible? Obvious, “the shoemaker's son always goes barefoot” ;). But don’t worry: the feature is here, moving the dependent properties discovering process to build-time, thanks to Fody.

As you probably know by now, Catel.Fody will rewrite all properties on the DataObjectBase and ViewModelBase. So, if a property is written like this:

public string FirstName { get; set; }

will be weaved into

public string FirstName
{
    get { return GetValue<string>(FirstNameProperty); }
    set { SetValue(FirstNameProperty, value); }
}

public static readonly PropertyData FirstNameProperty = RegisterProperty("FirstName", typeof(string));


But now we added a new feature to Catel.Fody. If a read-only computed property like this one exists:

public string FullName
{
    get { return string.Format("{0} {1}", FirstName, LastName).Trim(); }
}

the OnPropertyChanged method will be also weaved into

protected override void OnPropertyChanged(AdvancedPropertyChangedEventArgs e)
{
    base.OnPropertyChanged(e);
    if (e.PropertyName.Equals("FirstName"))
    {
        base.RaisePropertyChanged("FullName");
    }
    if (e.PropertyName.Equals("LastName"))
    {
        base.RaisePropertyChanged("FullName");
    }
}


This feature is already available in the latest beta package of Catel.Fody.

Try yourself and let us know.

Monday, June 30, 2014

Implementing notify property changed with SheepAspect

Introduction

I spend some time looking for AOP options for .NET.  All references point to PostSharp as the coolest option - even when you have to pay to use it - due to its features, starting from the way it works – in build time and static – plus a lot of build-in “advice” and extensions points.

There are also several run time and dynamic options such as Castle, Unity, etc., but I prefer the build time and static approach.  Indeed I use Fody - as an alternative to PostSharp - even when I have to write down custom plugins directly in IL.

But all of these AOP-like libraries and tools for .NET do not allow you to handle exactly the all the AOP concepts such as pointcut, join-point, and advice.

But recently I found a project in CodePlex, named SheepAspect with an introductory statement that includes the following words “…was inspired in AspectJ”. So, I just installed the package from NuGet and started to write in C# a notify property changed proof of concept with a friend of mine (Leandro).

Introducing NotifyPropertyChanged aspect

The very first step is to write a NotifyPropertyChangedAspect class. It’s important to get related with SAQL in order to query the right properties from the right types, and with the usage of the SheepAspect attributes. For this particular example the pointcut includes “all public property setters of types that implement System.ComponentModel.INotifyPropertyChanged” and look like this:
[Aspect]
public class NotifyPropertyChangedAspect
{
    [SelectTypes("ImplementsType:'System.ComponentModel.INotifyPropertyChanged'")]
    public void NotifiedPropertyChangedTypes()
    {
    }

    [SelectPropertyMethods("Public & Setter & InType:AssignableToType:@NotifiedPropertyChangedTypes")]
    public void PublicPropertiesOfTypesThatImplementsINotifyPropertyChangedInterfacePointCut()
    {
    }
}

Now, I just needed to add the notify property changed behavior as around advice just as follow:

[Around("PublicPropertiesOfTypesThatImplementsINotifyPropertyChangedInterfacePointCut")]
public void AdviceForPublicPropertiesOfTypesThatImplementsINotifyPropertyChangedInterface(PropertySetJointPoint jp)
{
    object value = jp.Property.GetValue(jp.This);
    if (!object.Equals(value, jp.Value))
    {
        jp.Proceed();
        jp.This.RaiseNotifyPropertyChanged(jp.Property);
    }
}

As you should notice, to implement this, I also introduce a couple of extension methods TryGetPropertyChangedField and of course the RaiseNotifyPropertyChanged itself:

public static bool TryGetPropertyChangedField(this Type type, out FieldInfo propertyChangedEvent)
{
    propertyChangedEvent = null;
    while (propertyChangedEvent == null && type != null && type != typeof(object))
    {
        propertyChangedEvent = type.GetField("PropertyChanged", BindingFlags.Instance | BindingFlags.NonPublic);
        if (propertyChangedEvent == null)
        {
            type = type.BaseType;
        }
        else if (!typeof(MulticastDelegate).IsAssignableFrom(propertyChangedEvent.FieldType))
        {
            propertyChangedEvent = null;
        }
    }

    return propertyChangedEvent != null;
}

/*...*/

public static void RaiseNotifyPropertyChanged(this object instance, PropertyInfo property)
{
    FieldInfo propertyChangedEvent;
    if (instance.GetType().TryGetPropertyChangedField(out propertyChangedEvent))
    {
        var propertyChangedEventMulticastDelegate = (MulticastDelegate)propertyChangedEvent.GetValue(instance);
        var invocationList = propertyChangedEventMulticastDelegate.GetInvocationList();
                
        foreach (var handler in invocationList)
        {
            MethodInfo methodInfo = handler.GetMethodInfo();
            methodInfo.Invoke(handler.Target, new[] { instance, new PropertyChangedEventArgs(property.Name) });
        }
    } 
}

Now, if a class that implements or inherits from a class that implements INotifyPropertyChanged interface is added, just like this one:

public class Person : INotifyPropertyChanged
{
        public event PropertyChangedEventHandler PropertyChanged;

        public string FirstName { get; set; }

        public string LastName { get; set; }

        public string FullName
        {
            get
            {
                return string.Format(CultureInfo.InvariantCulture, "{0} {1}", this.FirstName, this.LastName).Trim();
            }
        }
}

and a program like this one is written:

this.person.PropertyChanged += (sender, args) =>
                {
                    object value = sender.GetType().GetProperty(args.PropertyName).GetValue(sender);
                    Console.WriteLine("Property Changed => '{0}' = '{1}'", args.PropertyName, value);
                };

this.person.FirstName = "Igr Alexánder";
this.person.LastName = "Fernández Saúco";

then the output will be:

Property Changed => 'FirstName' = 'Igr Alexánder'
Property Changed => 'LastName' = 'Fernández Saúco'

Uhmm! But what about with the computed read-only properties like FullName?

Notifying property changes of computed read-only properties.

In order to notify changes of computed read-only properties, an inspection of the IL code is required. The .NET native reflection API is limited. From the PropertyInfo is only possible to get the IL byte array from the get method body, and nothing more. Therefore, I just switched to the Mono.Cecil reflection API to be able to complement the existing RaiseNotifyPropertyChanged extension method with the following extension methods:

public static bool ExistPropertyDependencyBetween(this Type type, PropertyInfo dependentProperty, PropertyInfo propertyInfo)
{
    AssemblyDefinition assemblyDefinition = new DefaultAssemblyResolver().Resolve(type.Assembly.FullName);
    TypeDefinition typeDefinition = assemblyDefinition.MainModule.GetType(type.FullName);
    PropertyDefinition dependentPropertyDefinition = typeDefinition.Properties.FirstOrDefault(definition => definition.Name == dependentProperty.Name);
    
    bool found = false;
    if (dependentPropertyDefinition != null)
    {
        MethodDefinition definition = dependentPropertyDefinition.GetMethod;
        if (definition.HasBody)
        {
            ILProcessor processor = definition.Body.GetILProcessor();

            int idx = 0;
            while (!found && idx < processor.Body.Instructions.Count)
            {
                Instruction instruction = processor.Body.Instructions[idx];
                
                MethodDefinition methodDefinition;
                if (instruction.OpCode == OpCodes.Call && (methodDefinition = instruction.Operand as MethodDefinition) != null && methodDefinition.DeclaringType.IsAssignableFrom(typeDefinition) && methodDefinition.Name == string.Format(CultureInfo.InvariantCulture, "get_{0}", propertyInfo.Name))
                {
                    found = true;
                }
                else
                {
                    idx++;
                }
            }
        }
    }

    return found;
}

/*...*/

public static IEnumerable<PropertyInfo> GetDependentPropertiesFrom(this Type type, PropertyInfo property)
{
    List<PropertyInfo> dependentPropertyInfos = type.GetProperties(BindingFlags.Instance | BindingFlags.Public).Where(dependentProperty => property != dependentProperty && dependentProperty.CanRead && type.ExistPropertyDependencyBetween(dependentProperty, property)).ToList();
    for (int i = 0; i < dependentPropertyInfos.Count; i++)
    {
        foreach (PropertyInfo info in type.GetDependentPropertiesFrom(dependentPropertyInfos[i]))
        {
            if (!dependentPropertyInfos.Contains(info))
            {
                dependentPropertyInfos.Add(info);
            }
        }
    }

    return dependentPropertyInfos;
}
just like this:
public static void RaiseNotifyPropertyChanged(this object instance, PropertyInfo property)
{
    FieldInfo propertyChangedEvent;
    if (instance.GetType().TryGetPropertyChangedField(out propertyChangedEvent))
    {
        var propertyChangedEventMulticastDelegate = (MulticastDelegate)propertyChangedEvent.GetValue(@this);
        var invocationList = propertyChangedEventMulticastDelegate.GetInvocationList();
                
        foreach (var handler in invocationList)
        {
            MethodInfo methodInfo = handler.GetMethodInfo();
            methodInfo.Invoke(handler.Target, new[] { instance, new PropertyChangedEventArgs(property.Name) });
        }

        foreach (PropertyInfo propertyInfo in instance.GetType().GetDependentPropertiesFrom(property))
        {
            foreach (var handler in invocationList)
            {
                MethodInfo methodInfo = handler.GetMethodInfo();
                methodInfo.Invoke(handler.Target, new[] { instance, new PropertyChangedEventArgs(propertyInfo.Name) });
            }
        }
    }
}

So, now the program output is:

Property Changed => 'FirstName' = 'Igr Alexánder'
Property Changed => 'FullName' = ' Igr Alexánder' 
Property Changed => 'LastName' = 'Fernández Saúco'
Property Changed => 'FullName' = ' Igr Alexánder Fernández Saúco'

Conclusion

At this point, you should be worried about the performance and a lot of reflection API calls. I'm pretty sure that this issue could be handle with the right caching approach ;).

I didn’t know why I never heard about SheepAspect before. Probably no one trust in something called “Sheep”, but believe me SheepAspect rocks!

Wait for a second! Right now I'm reading about something called mixing. Probably I can get a more declarative approach to implement this, just like the Fody or PostSharp home page examples. But I'm not sure right now, so, you have to wait for my next post to know about it ;).

X-ray StoneAssemblies.MassAuth with NDepend

Introduction A long time ago, I wrote this post  Why should you start using NDepend?  which I consider as the best post I have ever...