PostSharp Principles: Day 2 - Applying Aspects with Multicasting Part 1

by Dustin Davis on 28 Jun 2011

Download demo project source code

Yesterday we went over creating an aspect and applying it to a method. PostSharp is flexible in how aspects can be applied which results in outstanding benefits for us as developers. Imagine our exception wrapper aspect automatically being applied to any new method that is added to any class in the project. What about logging exceptions? No longer do we have to remember to add exception logging code to new methods. Of course, the practical applications are far beyond these simple scenarios but it gives you an idea of the power of PostSharp.

OnExceptionAspect is intended to work with methods. Yesterday we applied our aspect to the target method directly, which did the job but

  • What happens when we need to profile multiple methods?
  • What happens when we want to profile every method in every class?

That's a lot of work if we were to manually apply the aspect to each method.

OnMethodBoundaryAspect

PostSharp comes with another aspect that we can use called OnMethodBoundaryAspect. This class allows us to intercept certain points in a method’s execution by providing 4 virtual methods we can override

  • OnEntry - Before the execution of the method body
  • OnExit - Always called when a method is done executing even if there was an error
  • OnSuccess - Called only when a method is done executing and there were no exceptions
  • OnException - Called only when a method has stopped executing due to an unhandled exception

To give a clear understanding of multicasting, we’ll build a simple tracing aspect that will log the entry and exit of a method as it’s executed. Add a new file called MethodTraceAspect.cs and add the following code

[Serializable]
public class MethodTraceAspect : OnMethodBoundaryAspect
{
    public override void OnEntry(MethodExecutionArgs args)
    {
        Debug.WriteLine(args.Method.Name + " started");
    }

    public override void OnExit(MethodExecutionArgs args)
    {
        Debug.WriteLine(args.Method.Name + " finished");
    }
}

This aspect is the, “Hello World” of Aspect Oriented Programming, but it serves our purpose for today. When applied to a method this aspect will write ‘Started’ and ‘Finished’ to the output window. Go ahead and apply it to the GetByName method in InMemoryDataStore.cs file

[DatabaseExceptionWrapper]
[MethodTraceAspect]
public IQueryable GetByName(string value)
{
    var res = _contactStore.Where(c => c.FirstName.Contains(value) 
                    || c.LastName.Contains(value));

    if (res.Count() < 1)
    {
        ThrowNoResultsException();
    }

    Thread.Sleep(3000);
    return res.AsQueryable();
}

Run the application and do a search. As expected you see

GetByName started
GetByName finished

In the output window.

Class Level Declaration

Instead of applying the aspect manually to every method, we can apply the aspect only once, on the class. PostSharp will automatically apply the aspect to all methods in the class. Let's try it. Remove the [MethodTraceAspect] from GetByName method and apply to the InMemoryDataStore class

[MethodTraceAspect]
internal class InMemoryDataStore : PostSharpDemo1.IContactRepository
{
    …
}

When we run the application there is a lot more being displayed in the output window, just by starting the application.

.cctor Started
InitializeData Started
set__contactStore Started
set__contactStore Finished
get__contactStore Started
get__contactStore Finished
get__contactStore Started
get__contactStore Finished
get__contactStore Started
get__contactStore Finished
get__contactStore Started
get__contactStore Finished
get__contactStore Started
get__contactStore Finished
get__contactStore Started
get__contactStore Finished
InitializeData Finished
.cctor Finished
.ctor Started
.ctor Finished
GetAll Started
get__contactStore Started
get__contactStore Finished
GetAll Finished

Notice that there are calls to methods not in the class. Remember that property getters and setters are turned into methods when your code is compiled which makes them eligible for receiving the aspect. The .cctor entry is the static constructor and .ctor is the instance constructor.

As interesting as this is, you won’t always want to have the aspect applied to absolutely everything. There are a few ways to avoid aspect application on a specific target. We'll cover those tomorrow.

Conclusion

Today we saw another aspect we can take advantage of and we also saw how we can use multicasting to apply an aspect to multiple targets with one line of code. Tomorrow we’ll look at even more multicasting.

 

self[5]Dustin Davis is an enterprise solutions developer and regularly speaks at user groups and code camps.