Source Files for this tutorial: Advanced Example.cs Type Dependency.cs Role Dependency.cs

Advanced PostSharp : Introduction to Aspect Dependency

This article will build upon the previous article about PostSharp, Introduction to PostSharp and will explain the role of aspect dependency in aspect development under PostSharp and how aspect dependency of framework contracts can be specified.

When developing aspects under PostSharp, multiple aspects can be specified on any target. Unlike normal .NET attributes which only supply meta-data information, aspects play an important role in execution. This means that unlike normal .NET attributes that the order of attributes has no relevance to the compiled code, aspects on the other hand, must be ordered appropriately. The order of aspects might not be important in many scenarios, but on the other hand, in many scenarios an explicit order is necessary. PostSharp requires that, aspects themselves supply information about their order of execution, otherwise, PostSharp compiler will generate a warning, that, the order of execution for the aspects is undeterministic.

This is why, It is crucial for aspect development to consider supplying information about aspect ordering and aspect dependencies. The namespace, PostSharp.Aspects.Dependencies is the namespace that contains enums and attributes to specify aspect dependency. Out of actions which is specifiable under PostSharp, the following are notable:
  • AspectDependencyAction.Order : Specifies that, a strict ordering of dependent aspects are required. That one of them must appear before/after the other dependent aspect.
  • AspectDependencyAction.Commute : Specifies that, dependent aspects are communicative and either of them can appear before/after the other dependent aspect.
  • AspectDependencyAction.Conflict : Specifies that, dependent aspects conflict with each other, and presence of both dependent aspects on a target must be prohibited by the compiler.

There are 3 models provided by PostSharp to specify a dependency. Don't worry if you don't understand them at the moment. We will explain them in a moment using examples.
  • Type Dependency: This kind of dependency occurs between exactly two strictly typed aspects.
  • Role Dependency: This kind of dependency occurs between an aspect and any other aspect that has a specified role.
  • Effect Dependency: This kind of dependency occurs between an aspect and any other aspect that has a specified effect.

Type Dependency Example

In this example, we will explain how to specify the ordering of applied aspects using PostSharp. Type dependency, Is a dependency among two typed aspects. Meaning that It can not state a dependency among some groups or kinds of aspects. The two aspects for which we want to specify a dependency, should both exist. This allows us to strictly mandate our ordering, without considering the order in which the user has specified aspects.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Reflection;
using PostSharp.Aspects;
using PostSharp.Aspects.Dependencies;

namespace ConsoleApplication5
{
    [Serializable]
    class HelloAttribute : OnMethodBoundaryAspect
    {
        public string Name { get; set; }

        public override void OnEntry(PostSharp.Aspects.MethodExecutionArgs args)
        {
            Console.WriteLine("Hello, " + Name);
        }
    }

    [Serializable]
    [AspectTypeDependency(AspectDependencyAction.Order,
        AspectDependencyPosition.After, typeof(HelloAttribute))]
    sealed class ByeAttribute : OnMethodBoundaryAspect
    {
        public string Name { get; set; }

        public override void OnEntry(PostSharp.Aspects.MethodExecutionArgs args)
        {
            Console.WriteLine("Bye, " + Name);
        }
    }

    class Program
    {
        [Bye(Name = "Jack")]
        [Hello(Name = "Jack")]
        static void FirstMethod()
        {
        }

        [Hello(Name = "Smith")]
        [Bye(Name = "Smith")]
        static void SecondMethod()
        {
        }

        static void Main(string[] args)
        {
            FirstMethod();
            Console.WriteLine();
            SecondMethod();
        }
    }
}

In the above example, observe that, AspectTypeDependencyAttribute has been applied to ByeAttribute, which specifies that, always, ByeAttribute should be placed after HelloAttribute by the compiler. In effect, both the static method, FirstMethod and SecondMethod first print a hello, and then print a bye on the console screen, without considering the order of applied attributes. If you take a closer look, FirstMethod has reversed order of applied attributes, first a ByeAttribute and then a HelloAttribute which is also in reverse with SecondMethod. But in the end, first a hello, and then a bye, is printed on the console screen because of the strict ordering we have specified.

Role Dependency Example

Role dependency, unlike type dependency, allows to specify a dependency action to a group of aspects. These aspects may be existent, or may appear as an extension or even user created aspects. Every aspect, can provide some roles, which are string values. Although standard roles are specified by PostSharp itself, see http://doc.sharpcrafters.com/postsharp/2.0/##PostSharp.chm/html/T_PostSharp_Aspects_Dependencies_StandardRoles.htm but any user supplied string value can be used. An aspect can provide a role, using ProvideAspectRoleAttribute class. and a role dependency, can be specified by AspectRoleDependencyAttribute class.
Consider the following 3 class definitions.
    [Serializable]
    [ProvideAspectRole("Hello")]
    sealed class HelloAttribute : OnMethodBoundaryAspect
    {
        public string Name { get; set; }

        public override void OnEntry(PostSharp.Aspects.MethodExecutionArgs args)
        {
            Console.WriteLine("Hello, " + Name);
        }
    }

    [Serializable]
    [ProvideAspectRole("Hello")]
    sealed class HiAttribute : OnMethodBoundaryAspect
    {
        public string Name { get; set; }

        public override void OnEntry(MethodExecutionArgs args)
        {
            Console.WriteLine("Hi, " + Name);
        }
    }

    [Serializable]
    [ProvideAspectRole("Bye")]
    [AspectRoleDependency(AspectDependencyAction.Conflict, "Hello")]
    sealed class ByeAttribute : OnMethodBoundaryAspect
    {
        public string Name { get; set; }

        public override void OnEntry(PostSharp.Aspects.MethodExecutionArgs args)
        {
            Console.WriteLine("Bye, " + Name);
        }
    }

As you can see, HelloAttribute and HiAttribute classes provide the role Hello which is conflicting with ByeAttribute meaning that the following two combination will result in compile error.

        [Bye(Name = "Jack")]
        [Hello(Name = "Jack")]
        static void FirstMethod()
        {
        }

        [Hi(Name = "Smith")]
        [Bye(Name = "Smith")]
        static void SecondMethod()
        {
        }

It is a good practice to place your roles as constants in a separate class for universal access. These roles are defined in Arcana.Contract.Roles class.

Effect Dependency

Effect dependencies can be used to specify dependencies among aspects that cause an effect. These effects are provided by PostSharp internally. These effects are defined in StandardEffects class. Usually specifying effect dependency is not needed. So I will not drive deeper in it.

Synchronization and Asynchronization Example

At the end of this article, I want to introduce an example code that can cause method asynchronization and also synchronization using monitors. And how we can add dependencies so that asynchronization is performed before synchronization. This is mandatory because when a method is called asynchronously, it is queued to threadpool. And when it is picked up, the actual method implementation is called. This is when we can add synchronization using a monitor. This example might seem strange since the two of them should be conflicting with each other. We just do this as a matter of sample. We will use role dependency for this example.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Reflection;
using PostSharp.Aspects;
using PostSharp.Aspects.Dependencies;
using System.Threading;

namespace ConsoleApplication5
{
    [Serializable]
    [ProvideAspectRole("APC")]
    sealed class AsynchronousAttribute : MethodInterceptionAspect
    {
        public override void OnInvoke(MethodInterceptionArgs args)
        {
            Program.PrintTrace("Queueing to thread pool");
            ThreadPool.QueueUserWorkItem(new WaitCallback(
                delegate(object none) {
                    Program.PrintTrace("Entering work item");
                    args.Proceed();
                    Program.PrintTrace("Exiting work item");
            }), null);
        }
    }

    [Serializable]
    [AspectRoleDependency(AspectDependencyAction.Order,
        AspectDependencyPosition.After, "APC")]
    sealed class SynchronizedAttribute : OnMethodBoundaryAspect
    {
        public override void OnEntry(MethodExecutionArgs args)
        {
            Program.PrintTrace("Entering critical section");
            Monitor.Enter(args.Instance);
        }

        public override void OnExit(MethodExecutionArgs args)
        {
            Monitor.Exit(args.Instance);
            Program.PrintTrace("Exiting critical section");
        }
    }

    class Program
    {
        public static void PrintTrace(string message)
        {
            Console.WriteLine("Thread {0}: {1}",
                Thread.CurrentThread.ManagedThreadId,
                message);
        }

        [Synchronized]
        [Asynchronous]
        public void MyMethod()
        {
            PrintTrace("In My Method");
        }

        static void Main(string[] args)
        {
            Program target = new Program();
            PrintTrace("In Main");
            target.MyMethod();
            Thread.Sleep(500);
        }
    }
}

The following should be a typical output of application:
Thread 1: In Main
Thread 1: Queueing to thread pool
Thread 3: Entering work item
Thread 3: Entering critical section
Thread 3: In My Method
Thread 3: Exiting critical section
Thread 3: Exiting work item
Press any key to continue . . .


As you can see from the application output, After the work item is called from the .NET Threadpool, the synchronization occurs.

Note that, AsynchronousAttribute is built using MethodInterceptionAspect which allows us to actually replace the implementation of a method, and the ability to call the next node in the chain of aspects and actual method implementation using MethodExecutionArgs.Proceed() method. This allows us to queue invocation of the next node in chain, to a threadpool and perform the operation asynchronously. The code for SynchronizedAttribute is simple, the OnEnter method enters a critical section, and OnExit method exits the critical section, even in the case of an unhandled exception to ensure that deadlocks don't happen.

Source Files:
Advanced Example.cs
Type Dependency.cs
Role Dependency.cs

Last edited Aug 31, 2010 at 4:12 AM by arcane_master, version 2

Comments

No comments yet.