Archive

Download demo project source code

Welcome to week 2 of PostSharp Principles. As we move forward we’ll continue to explore the features of PostSharp. We still have a lot to cover and just like last week, we’re going to explore two more aspects provided by PostSharp and we’ll look at what is going on under the hood as we go.

Before we get any deeper into PostSharp, it’s important to understand how PostSharp implements your aspects especially when multiple aspects are applied to a single target. Today we’ll have a look at how PostSharp implements OnMethodBoundaryAspect when applied to a method.

Getting Started

If you have not done so, you will need to download demo project source code and you will also need to download and install a copy of ILSpy. You are welcome to use Reflector as the two tools are very similar.

Example

Continuing with the demo project from last week, let’s look at the GetByName method in the InMemoryDataStore.cs file.

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(); }

Since this is a pretty basic method it will be easy to see how PostSharp applies our aspects. Build the project with no aspects applied and then open ILSpy. Once ILSpy is open, browse to the output folder for the demo project and select PostSharpDemo1.exe. Navigate down the namespace tree until you reach the GetByName method in the InMemoryDataStore class.

image

It looks exactly the same as our original code. Let’s apply an aspect and see what changes.

Just for demonstration purposes, let’s build a new aspect based on OnMethodBoundaryAspect. Add a new file called DemoAspect.cs and add the following code

[Serializable]
public class DemoAspect : OnMethodBoundaryAspect
{
    public override void OnEntry(MethodExecutionArgs args)
    {
        Console.WriteLine("OnEntry");
    }

    public override void OnExit(MethodExecutionArgs args)
    {
        Console.WriteLine("OnExit");
    }

    public override void OnSuccess(MethodExecutionArgs args)
    {
        Console.WriteLine("OnSuccess");
    }

    public override void OnException(MethodExecutionArgs args)
    {
        Console.WriteLine("OnException");
    }
}

Apply the aspect to the GetByName method and then rebuild the project. Back in ILSpy, click refresh (you might have to navigate back to the GetByName method). What you see now is much different than before.

image

There is a try/catch/finally added to the method and references to InMemoryDataStore.<>z__Aspects.a0 throughout. Let’s start at the top.

InMemoryDataStore.<>z__Aspects

So what is this? A simple explanation is that this is a helper class that PostSharp creates to hold references to your aspect(s) and an instance of MethodBase for each method in which an aspect is applied. a0 is a reference to an instance of our DemoAspect. Feel free to navigate around using ILSpy.

At the start of our method a call to InMemoryDataStore.<>z__Aspects.a0.OnEntry(null) is made. This is our OnEntry point, before the rest of our method body. Next is a new declaration of our return type called, ‘result’. Our original code didn’t contain a result variable. PostSharp added it because you cannot do a return from a try/catch block in MSIL. This is the same behavior as the C# compiler.

Inside of the try block is our original method body with only a minor change of setting the result variable with our query results. At the end of the try block is a call to InMemoryDataStore.<>z__Aspects.a0.OnSuccess(null) because at this point, all of our code has executed without throwing an error which means it was successful.

The catch block makes a call to InMemoryDataStore.<>z__Aspects.a0.OnException(null) for what should be an obvious reason. An exception has occurred and we wanted to handle that event in our aspect. After our call, the exception is rethrown.

The finally block makes a call to InMemoryDataStore.<>z__Aspects.a0.OnExit(null) because OnExit must always be called even if the method exited with an exception.

At last, the results are returned to the caller.

Adjustments

Let’s make some changes to our aspect. Update the code to the following

[Serializable]
public class DemoAspect : OnMethodBoundaryAspect
{
    public override void OnException(MethodExecutionArgs args)
    {
        Console.WriteLine("OnException");
        args.FlowBehavior = FlowBehavior.Continue;
    }
}

Now we’re only implementing OnException and we’ve changed it to set the FlowBehavior. When an exception occurs, we don’t want to rethrow, just continue on. Rebuild the project and refresh ILSpy.

image

We have a try/catch now instead of a try/catch/finally, but now the catch method is much larger. So what’s going on? Starting our catch block is an instantiation of MethodExecutionArgs and then a call to InMemoryDataStore.<>z__Aspects.a0.OnException(methodExecutionArgs). The reason why there is now a declaration for MethodExecutionArgs is because we need to have access to the properties whereas before, we did not.

Now we come to a switch construct which checks the FlowBehavior property of our MethodExecutionArgs instance. FlowBehavior.Default and FlowBehavior .RethrowException will rethrow while FlowBehavior.Continue (which is what we wanted to do) will return result as it is while FlowBehavior.Return will set the value of result to the value of MethodExecutionArgs.ReturnValue. At the bottom is a catch all jump to the IL_A1 label which will rethrow. This is incase FlowBehavior was set to an unrecognized value.

Play around with different implementations and logic to see how PostSharp changes the resulting code.

As you can see, PostSharp analyzes the code of the aspect and generates only the code that supports the feature actually used by the aspect. This feature is called the aspect optimizer.

Keep in mind that the Starter edition (formerly known as the Community edition) does not include the aspect optimizer, so it may produce much more code for the same aspect.

Conclusion

It is important to know what is happening to your code when you apply aspects. Today we saw an example of OnMethodBoundaryAspect and how the method body is modified to allow the aspect to work. As we continue the series, we’ll look under the hood to see what is going on when dealing with other aspect types and when applying multiple aspects to a single target.

 

self573Dustin Davis is an enterprise solutions developer and regularly speaks at user groups and code camps. He can be followed on Twitter @PrgrmrsUnlmtd or his blog Programmers-Unlimited.com

Download demo project source code

Yesterday we started looking at multicasting. Today we’ll go even further with assembly level declarations and how to prevent aspects from being applied.

Assembly Level Declaration

At the highest level we have assembly declarations. This is a powerful feature that allows us to achieve many tasks. Before we explore the details, let’s see it in action. Remove any aspect declarations you have currently.

Note: Depending on your own tastes, you can apply this assembly declaration in any file. Commonly, these are put in the AssemblyInfo.cs file, but they can go anywhere. I recommend putting them in relevant class files to keep the visibility or creating an AspectInfo.cs file to keep them in. Putting them in the AssemblyInfo.cs could be a problem since it's a rarely used file.

[assembly: PostSharpDemo1.MethodTraceAspect()]
namespace PostSharpDemo1
{
    …
}

It can be as simple as that. With no other options this declaration will apply our aspect to every method in every class. Run it and check out the results.

Oops! There was a problem. We can't build the project because PostSharp is trying to apply our aspect to our aspect. The result of this, at least in this case, would be a stack overflow.

Preventing Aspect Application

So how do we prevent an aspect from being applied to certain targets? There are a few ways to achieve this but the easiest and most direct way to explicitly declare the aspect on the target and setting the AttributeExclude property to true. To fix the build errors we have to apply this technique to our aspect class.

[Serializable]
[MethodTraceAspect(AttributeExclude = true)]
public class MethodTraceAspect: OnMethodBoundaryAspect
{
    …
}

Do the same thing for DatabaseExceptionWrapper. Build and run. The output window is filled with every method, constructor and property call made.

The AttributeExclude will work at the class and assembly level as well. Since there are far too many properties in this project to manually add exclusions to each one, we can declare an assembly level exclude

[assembly: PostSharpDemo1.MethodTraceAspect(AttributeExclude = true, AttributePriority = 0, 
            AttributeTargetMemberAttributes = MulticastAttributes.CompilerGenerated)]

The result is the exclusion of property getter and setter methods. When we run the application now, we no longer see them in the output window.

Note: Using the MulticastAttributes.CompilerGenerated will affect more than just properties; anything that gets transformed by the compiler during the build will be affected.

Aspect Priority

We’ve added a new parameter to this declaration called AttributePriority. Application of attributes is undeterministic so there is no guarantee of execution order. If you look at the Warnings when you build the demo project you’ll see warnings from PostSharp about this.

Aspects do different things and the order is which they are applied can affect how they operate. It’s very important to keep this in mind when applying multiple aspects to a target.

We can set the AttributePriority with an integer value to determine the priority. The lower the value is, the higher the priority of the aspect. Higher priority aspects get applied first. Example:

[MethodTraceAspect(AttributePriority = 10)]
[DatabaseExceptionWrapper(AttributePriority = 20)]
public IQueryable GetByName(string value)
{
    …
}

We're telling PostSharp to apply the MethodTraceAspect first and then apply the DatabaseExceptionWrapper second. I used 10 and 20 because it gives better flexibility when you need to apply more aspects. You won’t have to adjust the priority values for each aspect this way. Feel free to use your own system.

Note that using AttributePriority is not considered best practice, because it quickly becomes difficult to manage. We’ll see another day how to address this issue in a clean and robust way.

Assembly Level Part 2

To wrap up this post, let's cover some of the options when declaring assembly declarations. To limit the scope of aspect application we can use the AttributeTargetTypes property to declare a specific type or namespace.

Namespace and Types

[assembly: PostSharpDemo1.MethodTraceAspect(
                       AttributeTargetTypes = "PostSharpDemo1.InMemoryDataStore")]

This declaration will apply the aspect only to the InMemoryDataStore type. The result is the same as decorating the class manually. This is useful when specifying an abstract class because the aspect is applied to all derived classes.

Wildcards
We can also declare wildcards. Specifying an asterisk on part of a namespace will apply the aspect to all types under that namespace and all types in sub namespaces.

[assembly: PostSharpDemo1.MethodTraceAspect(
                       AttributeTargetTypes = "PostSharpDemo1.Data.*")]

Any qualified type under PostSharpDemo1.Data namespace will receive the aspect.

Regular Expressions
PostSharp also lets us use regular expressions to declare target types.

[assembly: PostSharpDemo1 MethodTraceAspect(AttributeTargetTypes = " regex:.*Memory.*")]

Any qualified type with ‘Memory’ in the type name will receive the aspect.

By name
Last but not least, we can specify the exact name of the target. In this case we can declare

[assembly: PostSharpDemo1.MethodTraceAspect(AttributePriority = 10
                            AttributeTargetMembers="GetByName")]

[assembly: PostSharpDemo1.MethodTraceAspect(AttributePriority = 10,
                            AttributeTargetMembers="GetBy*")]

[assembly: PostSharpDemo1.MethodTraceAspect(AttributePriority = 10,
                            AttributeTargetMembers="regex:GetBy.*")]

AttributeTargetMembers is where we declare the name criteria for members.

For example, if we wanted to exclude all properties but using a more specific method than specifying MulticastAttributes.CompilerGenerated we can use the following regular expression to filter any method starting with get_ or set_ since these are what the compiler automatically prefixes getter/setter methods with

[assembly: PostSharpDemo1.MethodTraceAspect(AspectPriority = 10)]
[assembly: PostSharpDemo1.MethodTraceAspect(AspectPriority = 0, 
    AttributeExclude = true, AttributeTargetMembers = "regex:get_.*|set_.*")]

Conclusion

Not by any means did we cover the breadth of applying aspects but you should now have a good grasp on how to get up and running quickly. Being able to declare what will receive an aspect with one single line of code frees up our time and reduces clutter and complexity.

 

self[5][7]Dustin is an enterprise solutions developer and regularly speaks at user groups and code camp. He can be followed on twitter @PrgrmrsUnlmtd or his blog Programmers-Unlimited.com

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. He can be followed on twitter @PrgrmrsUnlmtd or his blog Programmers-Unlimited.com