Archive

Aspects have a lifetime as well as a scope just like classes in your project. Understanding this cycle is important for producing quality aspects as well as getting aspects to do what you need them to do. Today we’re going to cover the lifecycle.

Aspect Lifetime

So far we’ve built aspects and we’ve seen the results in action. What we haven’t seen is what happens between those two points. After the build process has been completed by MSBuild, PostSharp starts up and starts processing our aspects and aspect declarations.

Aspects go through two phases, compile time initialization and run time initialization.

Note: For Silverlight, Windows Phone and .NET CF, aspects are initialized at run time and all compile time steps are skipped.

Compile time

One of the benefits of using PostSharp is that it initializes aspects at compile time. Any expensive work that needs to be done related to the aspect can be done at compile time instead of run time. The build process may take a hit on performance, but run time does not suffer.

A new aspect instance is created for every target to which the aspect is applied. Each aspect goes through compile time validation and initialization. Aspect instances are then serialized into a resource for consumption at run time.

Compile time Validation

Last week we covered multicasting which is a way to apply an aspect to multiple targets. Even though multicasting gives us flexibility in applying aspects to targets, there are times when application of an aspect to a target must be considered using logic.

All aspects have a CompileTimeValidate virtual method. When implemented in the aspect, PostSharp will call this method to determine if the application of the aspect should continue on the given target. Depending on the type of aspect being implemented, CompileTimeValidate parameter(s) will be reflection information about the current target PostSharp is asking about. For example, Method based aspects like OnExceptionAspect and OnMethodBoundaryAspect will have a parameter of type MethodBase while LocationInterceptionAspect will have a parameter of type LocationInfo. If the aspect has been applied on an invalid target, implementations of this method must return false so this target will be silently ignored. Implementations can emit errors and warning by using Message.Write.

Compile time Initialization

All aspects also have a CompileTimeInitialize virtual method that can be implemented to perform expensive operations and/or initialize serializable fields so they are available at run time. PostSharp provides CompileTimeInitialize with the reflection information about the current target as well as information about the current aspect. Remember that since this is compile time, you won’t have access to the actual instance of the targets.

Run time

Before any aspect can be executed, PostSharp has to deserialize the aspects and initialize them. Since the serialization process uses a binary serializer, the aspect’s constructor is not called. The only way to perform initialization tasks at run time is to implement the RunTimeInitialize virtual method. When implementing RunTimeInitialize, you have access to the reflection information for the target but unlike CompileTimeInitialize, you have access to the instance of the target, not just the Meta data.

Example: Caching

We’re going to borrow the caching aspect from Matthew Groove’s post 5 Ways That Postsharp Can SOLIDify Your Code: Caching for this example.

[Serializable]
public class CacheAttribute : MethodInterceptionAspect
{
    [NonSerialized]
    private static readonly ICache _cache;
    private string _methodName;

    static CacheAttribute()
    {
        if (!PostSharpEnvironment.IsPostSharpRunning)
        {
            // one minute cache
            _cache = new StaticMemoryCache(new TimeSpan(0, 1, 0));
            // use an IoC container/service locator here in practice
        }
    }

    public override void CompileTimeInitialize(MethodBase method, AspectInfo aspectInfo)
    {
        _methodName = string.Format("{0}.{1}", method.DeclaringType.Name, method.Name);
    }

    public override void OnInvoke(MethodInterceptionArgs args)
    {
        var key = BuildCacheKey(args.Arguments);
        if (_cache[key] != null)
        {
            args.ReturnValue = _cache[key];
        }
        else
        {
            var returnVal = args.Invoke(args.Arguments);
            args.ReturnValue = returnVal;
            _cache[key] = returnVal;
        }
    }

    private string BuildCacheKey(Arguments arguments)
    {
        var sb = new StringBuilder();
        sb.Append(_methodName);
        foreach (object argument in arguments.ToArray())
        {
            sb.Append(argument == null ? "_" : argument.ToString());
        }
        return sb.ToString();
    }
}

Notice the _cache field is marked as NonSerialized. Fields that are not initialized at compile time or are not serializable should be decorated with NonSerializable. This aspect has a static constructor to instantiate _cache to a new instance of StaticMemoryCache. Static constructors are called even when doing binary serialization.

CompileTimeInitialize is where the cache key prefix is created. In this case it’s just the method name for the target. The values stored in fields/properties at compile time will be serialized into the aspect for consumption at runtime.

The OnInvoke method starts out by building a cache key which consists of the method name of the target method (set at compile time) and the value of each method argument. If the cache contains a valid object for the generated key then the value is returned from cache. If not, then the method is invoked and the return value is stored in cache.

A caching aspect is beneficial on expensive operations such as complex computations or frequently accessed data from a database. We’ll use it on our MD5 hash computation method

class Program
{
    static void Main(string[] args)
    {
        TestClass tc = new TestClass();
        Console.WriteLine(tc.GetMD5Hash("PostSharp"));
        Console.WriteLine(tc.GetMD5Hash("SharpCrafters"));
        Console.WriteLine(tc.GetMD5Hash("PostSharp"));
        Console.WriteLine(tc.GetMD5Hash("SharpCrafters"));

        Console.ReadKey();

    }
}

class TestClass
    {
        public TestClass() { }

        [Cache]
        public string GetMD5Hash(string value)
        {
            MD5 md5 = System.Security.Cryptography.MD5.Create();
            byte[] inputBytes = System.Text.Encoding.ASCII.GetBytes(value);
            byte[] hash = md5.ComputeHash(inputBytes);

            StringBuilder sb = new StringBuilder();
            for (int i = 0; i < hash.Length; i++)
            {
                sb.Append(hash[i].ToString("X2"));
            }
            return sb.ToString();
        }
            
    }

The first two calls will result in a computation but the second two will return the value from cache without the original method being called.

Just for fun, set break points on the CompileTimeValidate and CompileTimeInitialize methods. Notice they are never hit even if you run the application. Let’s have a look at what happened after we compiled.

No aspects applied

Aspects applied

image image

Looking at the compiled executable with ILSpy, we can see that compiling with our aspect applied produces a Resources node on the namespace tree which contains a binary file. Remember, in CompileTimeInitialize the private field _methodName was populated with the target method name. If you browse around ILSpy you won’t find any trace of that value. If you look at the binary file under Resources however, we see that the value was serialized along with the aspect instance.

image

PostSharp has initialized the instances of the aspect for each target, and then serialized them into a resource for consumption at run time.

Conclusion

You should now have a good idea about the process between writing an aspect and seeing it in action. Understanding the process of how an aspect is initialized is important when rolling custom aspects. Today we only covered the first half. Tomorrow we’re going to cover aspect scope.

 

self573_thumb[1]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

Download demo project source code

Today we’re going to look at two interception aspects, MethodInterceptionAspect and LocationInterceptionAspect. Due to the nature of these aspects we will break it into two parts. Today we’ll focus on using them and tomorrow we’ll look under the hood and cover chaining.

Interception

Interception aspects are a bit different from the other aspects we’ve looked at. OnExceptionAspect and OnMethodBoundaryAspects essentially wrap the original method body while interception aspects actually replace the method body with a call to the aspect, then moves the original body elsewhere. A simple diagram will help to visualize the process.

image

There are many beneficial processes that can be implemented using interception such as wait/retry, automatic thread delegation, lazy loading and validation.

MethodInterceptionAspect: Adding Multi-Thread Support

Our demo project has a problem of freezing the UI when doing a search. To fix this we must move the call to retrieve the data from the UI thread into its own thread. But, when dealing with the UI, controls cannot be accessed by any thread other than the thread they were created. This is a problem because we are updating a list control with the results of our query. We would get a cross-thread exception if we just moved the whole process to a new thread. We have to make sure that controls are updated on the correct thread.

Add a new file called WorkerThread.cs and add the following code

[Serializable]
public class WorkerThread : MethodInterceptionAspect
{
    public override void OnInvoke(MethodInterceptionArgs args)
    {
        BackgroundWorker bw = new BackgroundWorker();
        bw.DoWork += new DoWorkEventHandler(
            (object sender, DoWorkEventArgs e) =>
            {
                args.Proceed();
            }
        );

        bw.RunWorkerAsync();
    }
        
}

Now add a new file called UIThread.cs and add the following code

[Serializable]
public class UIThread : MethodInterceptionAspect
{
    private delegate void InvokeDelegate();

    public override void OnInvoke(MethodInterceptionArgs args)
    {
        Form main = (Form)args.Instance;

        if (main.InvokeRequired)
        {
            main.BeginInvoke(new Action(args.Proceed));
                
        }else {
            args.Proceed();
        }
    }
}

Our WorkerThread aspect is going to move our method call into a worker thread and execute it so our UI will no longer freeze. UIThread will take the method call and invoke it on the UI thread if it isn’t already on the UI thread so that we don’t receive cross-thread exceptions.

Open the ContactManager.cs code behind and apply the WorkerThread aspect to UpdateContactList() and UIThread to PopulateContactsList().

[WorkerThread]
private void UpdateContactList()
{
…
}

[UIThread]
private void PopulateContactsList(IQueryable contacts)
{ 
…
}

UpdateContactList() is where our query is called from so we want this to run in its own thread as to not interfere with the UI. PopulateContactList() handles manipulation of the control so we have to have the operations done on the UI thread.

Now run the application. As expected, the UI no longer locks up when doing a search.

MethodInterceptionAspect.OnInvoke

MethodInterceptionAspect provides a virtual method, OnInvoke, for us to implement. This method is called in place of the target method call. It is here where we determine if and how the target method is going to be invoked. We do this using the provided MethodInterceptionArgs.

MethodInterceptionArgs

MethodInterceptionArgs provides us with a few things such as access to the arguments passed to the method, the return value, the instance on which the method is being invoked and the method info just as we saw with MethodExecutionArgs when using OnMethodBoundaryAspect.

Since OnInvoke is called instead of the target method, MethodInterceptionArgs also provides ways to continue or change the invocation of the target method

MethodInterceptionArgs.Proceed

The body of the target method is moved into a new method which we can call using MethodInterceptionArgs.Proceed(). As in our threading aspects we made a call to MethodInterceptionArgs.Proceed() inside of our delegate to continue the invocation of the original method on our new thread. Execution will continue passing in the arguments held in MethodInterceptionArgs.Arguments and storing the return value in MethodInterceptionArgs.ReturnValue.

MethodInterceptionArgs.Invoke

When changes to the invocation are required, we can use the MethodInterceptionArgs.Invoke() method to pass in a different set of arguments to the target method.

LocationInterceptionAspect

Whereas the name MethodInterceptionAspect implies its usage, LocationInterceptionAspect does not. LocationInterceptionAspect is meant to be used on properties and fields. Just in case you’re wondering, the PostSharp documentation explains the ‘Location’ part of the name as

…called locations because they [fields & properties] both have the semantics of a slot where a value can be stored and retrieved.

How does it work?

LocationInterceptionAspect uses a pattern similar to the MethodInterceptionAspect which is replacing the body of the getter and setter methods (remember, in .Net properties are turned into a get and set method at compile-time) and replacing it with a call to the aspect’s OnGetValue and OnSetValue methods respectively.

After reading that last paragraph you might be thinking how that is supposed to work on fields (which don’t have getters and setters). When aspects based on LocationInterceptionAspect are applied to fields, they are turned into properties of the same name, scope and visibility as the original field.

Simple example

To clear up any confusion, let’s go through a trivial example. We’ll build an aspect and a simple class with a single property to demonstrate. We’ll start with the aspect.

[Serializable]
public class DemoAspect : LocationInterceptionAspect
{
    public override void OnGetValue(LocationInterceptionArgs args)
    {
        Debug.WriteLine("Get interception by aspect on " + args.LocationName);
        args.ProceedGetValue();
    }

    public override void OnSetValue(LocationInterceptionArgs args)
    {
        Debug.WriteLine("Set interception by aspect on " + args.LocationName);
        args.ProceedSetValue();
    }
}

All we’re doing is writing to the console when we reach a specific point in the process. Now let’s write a test class and apply our aspect.

public class TestClass
{
    private int _myProperty;
    [DemoAspect]
    public int MyProperty 
    {
        get { Debug.WriteLine("Get MyProperty"); return _myProperty; }
        set { Debug.WriteLine("Set MyProperty"); _myProperty = value; } 
    }

    public void Test()
    {
        MyProperty = 1; //Set the property value
        int x = MyProperty; //Get the property value
    }
}

With the aspect applied to our property, any call to the getter or setter will be intercepted and our aspect will take over. The output from calling the Test method on our class will be

Set interception by aspect on MyProperty
Set MyProperty
Get interception by aspect on MyProperty
Get MyProperty

LocationInterceptionAspect: IoC Resolution

One of the great uses I’ve found for LocationInterceptionAspect is lazy loading. Lazy loading can be used to initialize an object only when it’s first requested. Initialization can be a simple new-up or it can also be from an IoC container. The advantages of using an aspect to lazy load from an IoC container are

1. Fields & properties do not have to be public.

2. IoC ignorance. Cleanliness as the IoC specific code is no longer in the class.

What you need

Before you start, you will need to install an IoC container. We’re going to use StructureMap which you can download here. If you’re not familiar with IoC containers, that’s ok. We won’t cover what and why concerning IoC containers, but we will walk through setting up StructureMap and using it. Once you’ve downloaded StructureMap, reference it in the demo project.

Build It

The first thing we need to do is to register a type with the IoC container. We’ll just do this in the Main method in Program.cs

static void Main()
{
	ObjectFactory.Initialize(x =>
	{
		x.For(typeof(IContactRepository)).Use(typeof(InMemoryDataStore));
	});

	Application.EnableVisualStyles();
	Application.SetCompatibleTextRenderingDefault(false);
	Application.Run(new ContactManager());
}

All we’re doing is telling the container that whenever we request a type of IContactRepository, we want to get back an instance of InMemoryDataStore class. Simple as that.

Now we build our aspect. Add a new file called IoCResolution.cs and add the following code

[Serializable]
public class IoCResolution : LocationInterceptionAspect
{
    public override void OnGetValue(LocationInterceptionArgs args)
    {
        args.ProceedGetValue();
        if (args.Value == null)
        {
            object obj = ObjectFactory
.GetInstance(args.Location.PropertyInfo.PropertyType); args.Value = obj; args.ProceedSetValue(); } } }

Lazy load needs to happen when the field/property is requested for read so we implement the OnGetValue method to do our work. We start off by making a call to args.ProceedGetValue which will populate args.Value with the current value of the field/property. If args.Value is null then we request a new instance of the property type from the IoC container and then we set args.Value with our new object. A call to args.ProceedSetValue to finalize the change and we’re done.

Now that our aspect is done we need to make some small changes to ContactManager.cs code behind. First, apply our new aspect to the declaration of the contactRepository and then remove the instantiation of contactRepository from the ContactManager constructor.

[IoCResolution]
IContactRepository contactRepository;
private string searchCriteria = string.Empty;

public ContactManager()
{
	InitializeComponent();
}

Notice Visual Studio puts a blue squiggle under contactRepository to show that its default value will always be null (this is because Visual Studio does not detect an instantiation anywhere).

Go ahead and run the application. As expected, the application makes queries using contactRepository even though we have not explicitly instantiated it.

LocationInterceptionAspect: OnGetValue, OnSetValue

When building aspects based on LocationInterceptionAspect you’ll implement OnGetValue and/or OnSetValue to intercept a get request or set request respectively. Just with MethodInterceptionAspect, the body of the getter and setter are moved to a binding and is replaced with a call to the aspect’s OnGetValue from the getter or OnSetValue from the setter.

LocationInterceptionArgs

Both methods give you access to LocationInterceptionArgs which you will use to manipulate the invocation and the field/property.

LocationInterceptionArgs.GetCurrentValue

Gets the current value of the field/property by invoking the next node in the chain.

LocationInterceptionArgs.ProceedGetValue

Invokes the Get accessor of the next node in the chain. If a single interception aspect is applied, then the body of the getter will be invoked. This method stores the field/property value in LocationInterceptionArgs.Value.

LocationInterceptionArgs.SetNewValue

Sets the field/property to a different value by invoking the next node in the chain.

LocationInterceptionArgs.ProceedSetValue

Invokes the Set accessor of the next node in the chain. If a single interception aspect is applied, then the body of the setter will be invoked. Then new field/property value is set to the value LocationInterceptionArgs.Value.

LocationInterceptionArgs.Index

Index is provided when the property takes parameters. This usually means an indexer. Index is of type arguments and can be iterated over to get the index values passed. This is not for properties of an array or collection type.

LocationInterceptionArgs.Instance

Instance is a reference to the instance from which the invocation is occurring. This property is null if the field or property is static.

LocationInterceptionArgs.Location

Location contains all of the reflection data for the field/property. A LocationInfo wraps a PropertyInfo or FieldInfo.

LocationInterceptionArgs.Value

In the OnGetValue method, args.Value is null until a call to ProceedGetValue(). After ProceedGetValue() returns, args.Value will contain the current value of the field/property. In the OnSetValue method, args.Value will contain the proposed value provided by the caller.

Conclusion

Now that we went over two great examples of interception, we need to understand what happens under the hood, especially when multiple interception aspects are combined on a single target. Tomorrow we’ll grab our shovels and dig deep into how PostSharp implements our interception aspects.

 

self573_thumb[1]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

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