Wednesday, February 20, 2008

This is the second of a series about how to go about using postcompilation in your solutions. You can read it as a tutorial on how to use PostSharp. I am very much a new to that framework, but the power it provides could seriously change how you build your applications. While working on the EF contrib project, I had to dive into PostSharp, and I hope to share some of the things I learned along the way.

This post introduces the first real step I took: the compound aspect.

The full table of contents:

  • Introducing Entity Framework Contrib- Easy IPoco implementation V 0.1
  • Part II, Postsharp Core versus Postsharp Laos
  • Part III, the compound aspect
  • Part IV, the PocoInterfaceSubaspect
  • Part V, hooking up the weaver
  • Part VI, the EdmScalarWeaver
  • Recap

    We wish to create an attribute that can be placed on top of our ordinary Poco class, that will magically transform it into a class that implements the 3 IPoco interfaces. These are needed by the Entity Framework to do it's work. We will use PostSharp to do this.
    Post 1 introduced the project and post 2 introduced PostSharp.

    The Compound aspect

    I have created an attribute class that can be placed on top of other classes, like so:

        /// <summary>
        /// <para>
        /// Attribute that can be used to decorate normal POCO classes with. It is used to start off a post compilation phase
        /// that will modify the IL of the class. After this phase, the class will implement:
        /// <list type="">
        /// <item>INotifyPropertyChanged</item>
        /// <item>IEntityWithChangeTracker</item>
        /// <item>IEntityWithKey</item>
        /// <item>IEntityWithRelationships</item>
        /// </list>
        /// </para>
        /// <para>
        /// It will also place EDMScalarattributes on your properties.
        /// </para>
        /// <para>This results in a type that is completely ready for consumption by the EntityFramework</para>
        /// </summary>
        [AttributeUsage(AttributeTargets.Class, AllowMultiple = false, Inherited = false)]
        [MulticastAttributeUsage(MulticastTargets.Class, AllowMultiple = false)]
        public sealed class PocoAttribute : CompoundAspect
        {
    ... implementation
        }

    As you can see it inherits from the compound aspect class, from the PostSharp.Laos assembly.

    The compound aspect basically allows you to define other aspects to do the job for you. This is very nice when you want to do more things, but only want to use one aspect.

    By overriding the ProvideAspects method, we can add our other aspects:

      1         public override void ProvideAspects(object element, LaosReflectionAspectCollection collection)
      2         {
      3             // Get the target type.
      4             Type targetType = (Type)element;
      5
      6             // implement the INotifyPropertyChanged interface on the class
      7             collection.AddAspect(targetType, new AddNotifyPropertyChangedInterfaceSubAspect());
      8             // implement the three IPOCO interfaces on the class
      9             collection.AddAspect(targetType, new PocoInterfacesSubAspect());
    10             // inspect the complete class and add EDM scalar attributes to the properties
    11             collection.AddAspect(targetType, new EDMAttributesSubAspect(this.EDMContainerName, Name, NamespaceName, PathToConfigFile));
    12
    13             // iterate the properties
    14             foreach (PropertyInfo property in targetType.UnderlyingSystemType.GetProperties())
    15             {
    16                 if (property.DeclaringType == targetType && property.CanWrite)
    17                 {
    18                     MethodInfo method = property.GetSetMethod();
    19
    20                     if (!method.IsStatic)
    21                     {
    22
    23                         // throw notifypropertychanged events
    24                         collection.AddAspect(method, new OnPropertySetNotifyPropertyChangedSubAspect(property.Name, this.AspectPriority));
    25
    26                         // TODO: possibly refactor to only include edm properties
    27                         // call the changetracker
    28                         collection.AddAspect(method, new OnPropertySetChangeTrackSubAspect(property.Name, this.AspectPriority));
    29                     }
    30                 }
    31             }
    32         }

    As you can see, on line 4 I cast the element parameter to a type. That is the type we have adorned with our attribute (your 'Person' Poco class, for instance). Then, I specify that I want 3 other aspects to work on this type!

    • The AddNotifyPropertyChangedInterfaceSubAspect, will implement the INotifyPropertyChangedInterface. (note: this aspect is added just for simplicity, it has nothing to do with IPoco, so I might remove it).
    • The PocoInterfaceSubAspect, will implement the 3 interfaces (see following post)
    • The EDMAttributesSubAspect, will put EDMScalar attributes on top of our properties, needed by EF to do it's job

    After that, I loop through the properties in our targetType and add aspects to their setters. These will inject methods into the setters to throw the PropertyChanged event and let the EF changetracker know that the property was changed.

    That's all the work this compound aspect does. It just provides aspects to the correct codeblocks. The real work is done inside of these aspects and will be explained in following posts.

    Name
    E-mail
    Home page

    Comment (HTML not allowed)  

    Enter the code shown (prevents robots):