Showing posts with label Aspects. Show all posts
Showing posts with label Aspects. Show all posts

Friday, August 12, 2011

Aspect oriented programming with PostSharp and SharePoint 2010

The main purpose of aspect oriented programming in today’s software development processes is to separate the so called cross cutting concerns from the applications business logic. Things like logging or exception handling are used across all layers of an application. They should always be done in the same way and should not distract developers with their complexity. Aspect oriented programming can be used to achieve these goals. The following blog post shows an example of a caching aspect implemented with PostSharp and used inside a SharePoint 2010 WebPart. For an overview of implementing aspects with PostSharp have a look at http://www.sharpcrafters.com/postsharp/documentation. If you need more information on aspect orientation read http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.37.4987&rep=rep1&type=pdf or http://en.wikipedia.org/wiki/Aspect-oriented_programming.

Let’s start by creating a new Visual Studio solution. For the example I used the Visual Webpart template. After creation of the solution is finished we can directly start by creating the caching aspect. First of all a reference to the PostSharp library is needed. PostSharp comes with a library for different development environments. There is one library for Silverlight, one for developing applications with the compact framework and one for other .NET applications. I used the signed assembly for .NET 2.0. In order to make the assembly available to SharePoint we have to add it not only to the references of the project but also to the deployment package. With the new package windows in Visual Studio this can be achieved with a few clicks. Just open the package in the solution explorer. In the package window choose the ‘Advanced’ tab at the bottom:

On the following page you can add any existing assembly:

After adding the library and making the settings for deployment, we can now start to implement our aspect. PostSharp provides several base aspects that can be used to realize custom aspects. For the example I used the OnMethodBoundaryAspect. This aspect can be used to decorate a single method and contains the following base methods:

-          OnEntry
-          OnExit
-          OnSuccess
-          OnException
When you decorate a method with this aspect the preceding methods are respectively called on entering the method, on leaving the method or when an exception occurs. This makes it really comfortable to provide additional functionality for any given method. You can just write your attribute over a method and PostSharp will take care of linking the functionality of the aspect to the method. In order to achieve this, PostSharp integrates itself in the build process as a kind of post build event. That means that after building your assemblies, PostSharp takes them, searches for aspects and integrates the appropriate attribute implementation.

There are already some examples for caching aspects realized with PostSharp on the web. I used one from the sharpcrafters tutorial site. This aspect is designed for ASP.NET web applications so it also fits in the SharePoint context. Let’s have a look at the implementation and start with the OnEntry method:
public override void OnEntry(MethodExecutionArgs args)
{
    // Compute the cache key.
    string cacheKey = GetCacheKey(args.Instance, args.Arguments); 

    // Fetch the value from the cache.
    object value = HttpRuntime.Cache[cacheKey]; 

    if (value != null)
    {
        // The value was found in cache. Don't execute the method. Return
    immediately.
        args.ReturnValue = value;
        args.FlowBehavior = FlowBehavior.Return;
    }
    else
    {
        // The value was NOT found in cache. Continue with method execution, but
     store
        // the cache key so that we don't have to compute it in OnSuccess.
        args.MethodExecutionTag = cacheKey;
    }
}

The cache key for every decorated method is computed by the method name, its parameters and the concrete instance the method is executed in. This does not cover all cases, but it is sufficient for this example. With the fetched cache key, the runtime cache is accessed to provide the according value. If a value is found, this value will be returned from the method. Otherwise the computed cache key is temporarily stored in a Tags object which will be used in the OnSuccess method:
public override void OnSuccess(MethodExecutionArgs args)
{
    var cacheKey = (string)args.MethodExecutionTag;
    HttpRuntime.Cache[cacheKey] = args.ReturnValue;
}

The OnSuccess method takes the cache key that was built in the OnEntry method and stores the result of the executing method in the runtime cache. The next time the method is called with the same arguments, the aspect will provide the value directly from the cache instead of executing the whole method.
Using the caching aspect
With our caching aspect implemented we are now ready to use it. As with any attribute in .NET this is fairly easy. Just write the attribute over any method you want. In the example I had a method that returned an employee object from a SharePoint list for a given ID:
[Caching]
private static Employee GetEmployee(string id)
{
    using (var web = SPContext.Current.Web)
    {
        var list = web.GetList("/Lists/Employees");
        var item = list.GetItemById(Int32.Parse(id));

        if(item != null)
        {
            var result = new Employee
                                {
                                    Name = item["Title"].ToString(),
                                    Team = item["Team"].ToString(),
                                    Contribution = (double) item["Contribution"]
                                    //... properties left out for brevity
                                };
            return result;
        }
    }
    return null;
}

The retrieval of an employee happens only on the first call of the method. On the second call with the same ID the employee object is directly from the cache.