PostSharp Blog

The official blog of PostSharp: annoucements, tips & tricks

Tutorial
15 July 2011

Yesterday we introduced members into our target class. Today we’re going to go the other way and introduce members from our target class into our aspect for consumption by the process of importing.

Importing Members

There are cases when you will need to bring in a field or method from a target class so that you have access to it from within the aspect. What we’re doing is making assumptions about the target. It’s important to document what the aspect is assuming about the target code. Another approach instead of importing, is to cast the instance to a interface (or type) known to be implemented on the target.

To import, we use the ImportMember attribute. ImportMember allows us to import fields, properties, methods and events. As an example, let’s look at some sample code.

public interface IIdentifiable
{
    Guid ID { get; }
    void PrintID();

}

public class ExampleClass : IIdentifiable
{
    public Guid ID { get; private set; } //Satisfy the interface

    public ExampleClass()
    {
        this.ID = Guid.NewGuid();
    }

    [ImportExampleAspect]
    public void DoWork()
    {
        Console.WriteLine("Doing work");
    }

    public void PrintID()
    {
        Console.WriteLine(this.ID.ToString());
    }

}

[Serializable]
public class ImportExampleAspect : OnMethodBoundaryAspect, IInstanceScopedAspect
{
    [ImportMember("PrintID", IsRequired = true)]
    public Action PrintID;

    [ImportMember("ID")]
    public Property IDFromClass;

    public override void OnEntry(MethodExecutionArgs args)
    {
        Guid value = IDFromClass.Get();
        PrintID();
        Console.WriteLine("Value from IIdentifiable.ID is {0}", value.ToString());
    }

    public object CreateInstance(AdviceArgs adviceArgs)
    {
        return this.MemberwiseClone();
    }

    public void RuntimeInitializeInstance() {}

}

We have an example class that implements a simple interface and an aspect based on OnMethodBoundaryAspect. When you run this example code, the value of the ‘ID’ property will be written to the console.

178411e1-c79b-43fc-9aa0-ff309425abaa
Value from IIdentifiable.ID is 178411e1-c79b-43fc-9aa0-ff309425abaa
Doing work

Notice that our aspect implements the IInstanceScopedAspect interface. ImportMember cannot import static fields so we have to define our aspect as instance scoped. For more information on the life time and scope of aspects, refer to Day 9 and Day 10 of the PostSharp Principals series.

We expect the target to implement the interface which has a PrintID method defined. So we setup a public Action and we call it PrintID. We decorate our Action with the ImportMember attribute passing in the target’s member name which is ‘PrintID’.

Next we defined the IDFromClass public field of type Property<Guid>. This class has two properties: Get and Set. At runtime, the Get and Set properties will contain a delegate to the getter and setter of this property. In order to import an event, we need a public field of type Event<EventArgs>, which has two delegate properties: Add and Remove.

We do not have to use the same name as the target’s member name, so instead of ‘ID’ we are using ‘IDFromClass’.Inside of the OnEntry method we get the value of ID from our target class using the Get delegate provided by IDFromClass, make a call to the PrintID method and then write out the value to the console.

Note: ImportMember will work on members of any visibility, even private.

ImportMemberAttribute

There should be no surprise when I say that we can control the behavior of the import using the attribute’s parameters.

IsRequired

Specifies whether the member being imported is required or not. If true, a compiler error will occur if the member is not present in the target class. If false, the reference to the member will be null if it does not exist in the target.

Order

Order takes an enumeration from PostSharp.Aspects.Advices.ImportMemberOrder to determine when the importing will occur.

· AfterIntroductions (default) – Importing will occur only after the aspect has introduced its own members. This is useful if the imported member is virtual and we need to import the last override.BeforeIntroductions – Importing will occur before any members are introduced by the aspect. This gives you a chance to get a reference to the original member before any overriding occurs. This is similar to calling an overridden method by the “base” keyword.

· Default – The default is AfterIntroductions.

There can be any number of aspects on the same class that override the same method. Importing before the introductions allows you to call the next node in the chain of overwriting.

Better Example

Let’s have a look at a more in-depth example using both importing and introduction, the MakeDirtyOnChange aspect. When working with workspaces or MDI (multi-document interface) applications, many “documents” can be open at one time. There is a need to know when one or more of the “documents” have been modified so that you can inform the user visually and also to know which items need to be updated in the data store. Think about working with five C# files in Visual Studio. When a change is made to a file, you will see an asterisk next to the filename on the file’s tab. This is the indication that a change has been made and not yet saved.

Implementing the IsDirty pattern is similar to the INotifyPropertyChange, the code to wire up the change notification is redundant and tedious. Let’s check out our interface and test class before diving into our aspect

public interface IsDirty
{
    bool IsDirty { get; }
    event EventHandler WasMadeDirty;
    void ResetDirtyState();
    ReadOnlyCollection DirtyProperties { get; }
}

public class DirtyEventArgs : EventArgs
{
    public string DirtyProperty { get; private set; }

    public DirtyEventArgs(string dirtyProperty)
    {
        this.DirtyProperty = dirtyProperty;
    }
}

[MakeDirtyOnChange]
public class Document
{
    private Guid _docId = Guid.NewGuid();
    public Guid DocId { get { return _docId; } }

    public string Title { get; set; }
    public string Author { get; set; }
    public string Content { get; set; }
}

Our IDirty interface has a few requirements that we can and will use to determine if an item has changes. We specify a custom EventArgs class that allows us to provide details about the changes made to the item when invoking the WasMadeDirty event. Our Document class is a clean model that knows nothing about the IsDirty interface.

[Serializable]
[IntroduceInterface(typeof(IsDirty), OverrideAction = InterfaceOverrideAction.Ignore)]
public class MakeDirtyOnChange : InstanceLevelAspect, IsDirty
{
    [OnLocationSetValueAdvice, MulticastPointcut(Targets=MulticastTargets.Property)]
    public void OnValueChanged(LocationInterceptionArgs args)
    {
        MakeDirty(args.LocationName);
    }

    private bool _isDirty;
    private List _dirtyProperties;

    [ImportMember("SetDirty")]
    public Action MakeDirty;

    [IntroduceMember(IsVirtual=true, OverrideAction=MemberOverrideAction.Ignore)]
    public void SetDirty(string property)
    {
        _isDirty = true;
        if (WasMadeDirty != null)
        {
            WasMadeDirty.Invoke(this.Instance, new DirtyEventArgs(property));
        }
    }

    #region IsDirty Members

    [IntroduceMember(OverrideAction = MemberOverrideAction.Ignore)]
    public bool IsDirty { get { return _isDirty; } }

    [IntroduceMember(OverrideAction = MemberOverrideAction.Ignore)]
    public ReadOnlyCollection DirtyProperties { get { return _dirtyProperties.AsReadOnly(); } }

    [IntroduceMember(OverrideAction = MemberOverrideAction.Ignore)]
    public event EventHandler WasMadeDirty;

    [IntroduceMember(IsVirtual=true, OverrideAction=MemberOverrideAction.Ignore)]
    public void ResetDirtyState()
    {
        _isDirty = false;
        _dirtyProperties.Clear();
    }
 
    #endregion

    public override void RuntimeInitializeInstance()
    {
        _isDirty = false;
        _dirtyProperties = new List();
    }
}

We decorate our aspect with the IntroduceInterface attribute, specifying the IsDirty interface. Our aspect implements the IsDirty interface to satisfy the requirements and then we introduce those members to the target.

We setup a location interception using OnLocationSetValueAdvice attribute and specify the target is MulticastPointcut.Property ([MARKER, Advice link]). When a property is changed, we’re going to invoke the MakeDirty method which we tell PostSharp to import from the target’s “SetDirty” method, if it has one. We’re using the defaults for ImportMember which means the import will happen after our members are introduced. Since we’re introducing our own SetDirty method, MakeDirty will contain our SetDirty implementation if the target class did not already have its own implementation.

Since our aspect derives from InstanceLevelAspect we override the RuntimeInitializeInstance method and use it to initialize our private members to their default states.

We can use the following code to try out the aspect

class Program
{
    private static List _changedDocuments = new List();
    private static List _openDocuments = new List();

    static void Main(string[] args)
    {
        for (int i = 0; i < 5; i++)
        {
            Document doc = new Document();
            Post.Cast(doc).WasMadeDirty 
                += new EventHandler(doc_WasMadeDirty);

            _openDocuments.Add(doc);
        }

        _openDocuments[0].Author = "Dustin Davis";
        _openDocuments[0].Title = "PostSharp Principals - Day 1";

        _openDocuments[2].Author = "Dustin Davis";
        _openDocuments[2].Title = "PostSharp Principals - Day 3";

        Console.ReadKey();
    }

    static void doc_WasMadeDirty(object sender, DirtyEventArgs e)
    {
        Document doc = (Document)sender;

        if (_changedDocuments.Any(c => c.Equals(doc.DocId)))
        {
            return;
        }
            
        _changedDocuments.Add(doc.DocId);
        Console.WriteLine("Document {0} was modified.", doc.DocId);
    }
}

The code is pretty straight forward. We create five documents and then add them to our open documents collection. Finally we make changes to two documents. When we run the code, we see the following results

Document acbd247a-e742-499e-b27c-ee028e8e6789 was modified.
Document 9ded92cc-008f-48bc-b1b5-e1b0b967e42d was modified.

But wait, how are we handling the WasMadeDirty event? I’m glad you asked.

Post.Cast<>()

Post.Cast<>() allows us to cast an instance of a type to another type at design time. For example, our Document class doesn’t implement the IsDirty interface so we can’t access the IsDirty specific members unless we casting. We use the generic Cast<SourceType, TargetType>(SourceType Instance) method to give us back an instance of TargetType.

It’s basically nothing more than regular casting, but the difference is when you use Post.Cast<>() you receive compile-time errors if the cast cannot take place. The obvious benefit is that you know right away that the cast fails instead of at run time, potentially introducing bugs.

In the final result, the call to Post.Cast<>() is replaced with an actual cast.

Conclusion

Previous aspects we looked at have been pretty disconnected from the targets. Being able to introduce and import members gives us a connection and increased flexibility. Being able to automatically introduce interfaces and boiler plate code that is sometimes only consumed at run time frees us and keeps our code clean.

self573_thumb[1]Dustin Davis 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
Tutorial
14 July 2011

Today we continue our descent into the depths of the PostSharp framework to expose even greater powers to utilize in our projects.

Introduction

No, not the “Hello, my name is Dustin!” kind of introduction, but the “injection” type. What does that mean? PostSharp gives us the power to implement an interface on a class…at build time. We can also add (introduce) members to that class such as fields/properties, events and methods too. These members are injected at build time and are available at run time.

Why would you want to do this? As in most cases when applying aspect-oriented programming, you would use this to implement required interfaces that are little more than boilerplate code. One of the most popular examples of interface introduction is the NotifyPropertyChanged aspect which automatically introduces the INotifyPropertyChanged interface and required members. Anyone who has worked with WPF and the MVVM pattern would love to not have to write all of that scaffolding code just to get change notification. Since that aspect uses features we have not yet covered, we will not cover it today. If you’re feeling adventurous, you can check it out here.

Member Introduction

Member introduction allows us to add properties, events and methods to a class. Let’s start off by creating an aspect to introduce a property and a method.

[Serializable]
public class IntroduceAspect : InstanceLevelAspect
{
    [IntroduceMember]
    public int ID { get; set; }

    [IntroduceMember]
    public void SomeMethod(int param1)
    {
        Console.WriteLine("Inside of introduced method");
    }

}

And now our target class

[IntroduceAspect]
public class TargetClass
{

}

You might be laughing at our test class, but don’t worry, our aspect will do the work for us. When we look at the compiled assembly with ILSpy, we see that instead of a blank class we have a few more members than we started with, including the members we wanted to introduce.

image

Amongst the aspect related code, we have our ID property and our SomeMethod method. Notice that the getter and setter of ID are delegated to our aspect and so does our method. This is important to keep in mind because when implementing members, they must be marked as public inside of the aspect (because our target class has to access them). However, if you happen to forget, PostSharp will remind you with a compiler error

image

But what happens if you don’t want the introduced members to be public in the target class? Have no fear, PostSharp thought of that too. Let’s have a look at the IntroduceMember attribute.

IntroduceMember attribute

By default, using IntroduceMember by itself will use public visibility and will cause compiler errors if a member with the same signature is already part of the class. We can control the behavior of how the member is implemented by changing the following parameters.

Visibility

By default, PostSharp will introduce the member to the target class with public visibility. We can specify one of the enumerations from PostSharp.Reflection.Visibility to control what visibility the member will have in the target class. Available values are

· Public (Default) – Is publically available.

· Family – Is available to the class and any derived classes. Same as protected.

· Assembly – Is publicly available within the assembly. Same as internal.

· FamilyOrAssembly – Is available to the class and any derived classes, but only within the assembly. Same as protected internal.

· FamilyAndAssembly – Protected types inside the assembly. There is no C# equivalent.

· Private – Only visible to the class.

OverrideAction

There is a chance that the target class already has a member with the same signature. By default, there will be a compiler error if this scenario is encountered. To change the behavior, we can provide one of the enumerations from PostSharp.Aspects.Advices.MemberOverrideAction.

· Default – Fails with a compiler error.

· Fail – Fails with a compiler error.

· Ignore – Continues on, without trying to introduce the member.

· OverrideOrFail – Tries to override the member with our own implementation. If the existing member is defined in a base class and is sealed or non-virtual, it will fail with a compiler error.

· OverrideOrIgnore – Tries to override the member with our own implementation. If the existing member is defined in a base class and is sealed or non-virtual, it will ignore the member introduction and continue on.

IsVirtual

If you would like to introduced member to be virtual (overridable in derived classes) then you can set IsVirtual to true. The member signature in the base class will be marked as virtual.

CopyCustomAttributesAttribute

Sometimes members need to be decorated with attributes. An example of this would be decorating members of a DataContract with DataMember. However, when introducing members from an aspect, any attributes applied to the member in the aspect will not be introduced along with the member in the target. We can use CopyCustomAttributes attribute in addition to the IntroduceMember attribute to introduce the attributes along with the member. Let’s look at an example.

[Serializable]
public class IntroduceAspect : TypeLevelAspect
{
    [IntroduceMember]
    [DataMember(IsRequired=true)]
    public int ID { get; set; }
}

[IntroduceAspect]
[DataContract]
public class TargetClass
{
    [DataMember]
    public string FirstName { get; set; }
}

Our aspect is introducing a member, ID, which is decorated with DataMember. Let’s look at the result in ILSpy

image

The DataMember attribute is not present on ID. Let’s update the aspect to use CopyCustomAttributes.

[Serializable]
public class IntroduceAspect : TypeLevelAspect
{
    [IntroduceMember, CopyCustomAttributes(typeof(DataMemberAttribute), 
            OverrideAction = CustomAttributeOverrideAction.MergeReplaceProperty)]
    [DataMember(IsRequired=true)]
    public int ID { get; set; }

}

In the constructor for CopyCustomAttributes we pass in the base type for the desired attribute and then we set the override action with a value from the CustomAttributeOverrideAction enumeration. When we look at the end result in ILSpy, we see that the attribute was introduced along with the member.

image

CustomAttributeOverrideAction

CustomAttributeOverrideAction is an enum that lets us tell PostSharp how to handle a situation when an attribute of the same type already exists on the target member.

· Default – Fails with a compile time error.

· Fail – Fails with a compile time error.

· Ignore – Ignores the attribute introduction and does not generate an error.

· Add – Adds the attribute as defined, even if it already exists on the target. This could cause duplicate attributes on the target.

· MergeAddProperty – Combines the existing attribute with the attribute being introduced. Any properties defined by the existing attribute will remain. No override will occur. Any properties defined by the introduced attribute will be added to the existing attribute.

· MergeReplaceProperty – Same as MergeAddProperty except that any properties defined by the existing attribute will overridden by the introduced attribute.

Interface Introduction

When introducing an interface via an aspect, the interface must be implemented on the aspect itself. The type will expose the interface at run time, but the aspect actually implements it. Let’s have a look at our interface:

public interface IFriendlyName
{
    string Name { get; set; }
    void PrintName();
}

And now our aspect:

[Serializable]
[IntroduceInterface(typeof(IFriendlyName))]
public class IntroduceAspect : InstanceLevelAspect, IFriendlyName
{
    #region IFriendlyName Members

    public string Name { get; set; }
        
    public void PrintName()
    {
        Console.WriteLine(this.Name);
    }

    #endregion
}

Our test class remains the same, empty

[IntroduceAspect]
public class TestClass
{

}

When we look at the compiled result we see our interface has been implemented

image

Notice that we didn’t use the IntroduceMember attribute on the interface members. Also notice that the resulting implementations of the interface members are private. To make the interface members public we have to apply the IntroduceMember attribute to the members

[Serializable]
[IntroduceInterface(typeof(IFriendlyName))]
public class IntroduceAspect : InstanceLevelAspect, IFriendlyName
{
    #region IFriendlyName Members

    [IntroduceMember]
    public string Name { get; set; }

    [IntroduceMember]
    public void PrintName()
    {
        Console.WriteLine(this.Name);
    }

    #endregion
}

And now the compiled result shows two implementations of our members

image

Looking at the PrintName method, the explicit interface implementation is private, but we’ve introduced a public version which the interface method calls.

IntroduceInterface attribute

To tell PostSharp that we want to introduce an interface, we decorate the aspect with the IntroduceInterface attribute. To tell PostSharp which interface to implement, we pass in a type using typeof(IFriendlyName). Just like the IntroduceMember attribute, there are parameters to control the behavior of the introduction.

· OverrideAction – Exactly the same as IntroduceMember. Determines what to do when the target already implements the interface. Default is to fail with a compiler error.

· IsProtected – If set to true, the interface is not directly implemented by the type. Instead, the type exposes the interface through the IProtectedInterface<T>. Since protected interfaces are considered obsolete, you should leave this as false (default).

· AncestorOverrideAction – Defines the behavior of the introduction when and ancestor of the interface is already applied to the target class. See example below. Available enumerations in the PostSharp.Aspects.Advices.InterfaceOverrideAction are Default (Fail), Fail and Ignore.

Extended Example

Let’s finish up with a bit more in-depth example using some of the behavior parameters.

public interface IIdentifiable
{
    Guid ID { get; set; }
}

public interface IFriendlyName : IIdentifiable
{
    string Name { get; set; }
    void PrintName();
}

[IntroduceAspect]
public class TargetClass : IIdentifiable
{
    #region IFriendlyNameBase Members

    public Guid ID { get; set; }   

    #endregion

    string Name { get; set; }

    public void PrintName()
    {
        throw new NotImplementedException();
    }
}

[Serializable]
[IntroduceInterface(typeof(IFriendlyName), 
AncestorOverrideAction=InterfaceOverrideAction.Ignore)] public class IntroduceAspect : InstanceLevelAspect, IFriendlyName { #region IFriendlyName Members [IntroduceMember(OverrideAction=MemberOverrideAction.Ignore)] public Guid ID { get; set; } [IntroduceMember(OverrideAction=MemberOverrideAction.Ignore)] public string Name { get; set; } [IntroduceMember(OverrideAction=MemberOverrideAction.OverrideOrFail)] public void PrintName() { Console.WriteLine(this.Name); } #endregion }

We define two interfaces. IFriendlyName implements IIdentifiable. Our test class implements IIdentifiable and also has a PrintName method which throws an exception. Our aspect specifies the introduction of IFriendlyName and also implements the required interface members. We specify that we should ignore any implementation of an ancestor (IIdentifiable) of the introduced interface (IFriendlyName). We also specify that we want to ignore the member introduction on the two properties if they exist in the target class. We mark PrintName with the OverrideOrFail because we want to force our own implementation of the PrintName method. The end result looks like this

image

First take a look at the PrintName method. Instead of the original method body, which threw an exception, we see that there is a call to our aspect which invokes our implementation of that method.

Next we see that both interfaces are implemented, but we only have get/set methods for the Name property, not the ID property. This is because PostSharp ignored the implementation of IIdentifiable since it was already implemented on the target class. If we remove InterfaceOverride.Ignore from the IntroduceInterface attribute, we would get a compiler error.

image

If we removed the implementation of IIdentifiable from our test class, we would see get/set methods for ID in the compiled results.

image

Conclusion

Today we covered some good ground on introducing members and interfaces along with some of the nuances that you have to be aware of. Tomorrow we’ll continue with importing members and accessing introduced members at compile time.

self573_thumb[1]Dustin Davis 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
Tutorial
13 July 2011

When it comes to building complex aspects to solve a specific problem or implement a pattern, the base classes such as OnMethodBoundaryAspect and LocationInterceptionAspect aren’t always up-to the job. We covered the IAspectProvider yesterday, which allows us to dynamically tell PostSharp which aspects to apply to a target at compile time. Today we’re going to build complex aspects that encapsulate multiple transformations in a single aspect.

Advices and Pointcuts

Before we continue, we should cover some vocabulary. We’ve avoided the use of these terms until now to avoid any confusion.

· Advice – “An advice is anything that adds a behavior or a structural element to an element of code. For instance, introducing a method into a class, intercepting a property setter, or catching exceptions, are advices.”

· Pointcut – “A pointcut is a function returning a set of elements of code to which advices apply. For instance, a function returning the set of properties annotated with the custom attribute DataMember is a pointcut.”

When we override the OnEntry method when building an aspect based on OnMethodBoundaryAspect, we’re providing the advice to implement. By default the pointcut would be all methods in a class, if the class was decorated with our aspect based on the OnMethodBoundaryAspect base class.

PostSharp provides us with a set of attributes for declaring advice and pointcuts in any combination under a single aspect. Let’s have a look at an example to give a better picture

[Serializable]
public class ComplexAspect : TypeLevelAspect
{
    private int MethodCounter = 0; //Shared between all advices

    [OnMethodInvokeAdvice, MulticastPointcut(Targets =
                        MulticastTargets.Method, MemberName = "DoSomethingElse")]
    public void OnInvoke(MethodInterceptionArgs args)
    {
        Console.WriteLine("Before method {0}. MethodCounter = {1}", 
args.Method.Name, this.MethodCounter); args.Proceed(); Console.WriteLine("After method {0}. MethodCounter = {1}",
args.Method.Name, this.MethodCounter); } [OnMethodEntryAdvice, MulticastPointcut(Targets = MulticastTargets.Method)] public void OnEntry(MethodExecutionArgs args) { MethodCounter++; Console.WriteLine("Entering {0}. MethodCounter = {1}",
args.Method.Name, this.MethodCounter); } [OnMethodExitAdvice(Master = "OnEntry")] public void OnExit(MethodExecutionArgs args) { Console.WriteLine("Exiting {0}. MethodCounter = {1}",
args.Method.Name, this.MethodCounter); } [OnLocationSetValueAdvice, MulticastPointcut(Targets = MulticastTargets.Property)] public void OnPropertySet(LocationInterceptionArgs args) { MethodCounter++; Console.WriteLine("Setting property: {0} = {1}. MethodCounter = {2}",
args.LocationName, args.Value, this.MethodCounter); } }

Our aspect derives from TypeLevelAspect, not one of the base classes we’ve been using. We have four methods in our aspect and each method is decorated with an advice attribute along with a pointcut attribute.

The OnEntry method is decorated with the OnMethodEntryAdvice attribute which has the same semantics of overriding the OnEntry method in an OnMethodBoundaryAspect. The MulticastPointcut attribute is used and passed the MulticastTargets.Method flag to let PostSharp know that we want to apply this advice to methods in general.

Because we’re using TypeLevelAspect instead of OnMethodBoundaryAspect, we are able to share state between advices. When using OnMethodBoundaryAspect, an instance of our aspect is created for each target. For example, Method1 would have its own copy of our aspect and Method2 would have its own copy. Using TypeLevelAspect to implement our advices changes that behavior; we have a single instance of our aspect that is used for each target which means that the advices get to share state. We’re going to demonstrate this using the MethodCounter field to increment on each method entry and display its value throughout the other advices.

Notice the OnExit advice isn’t specifying a pointcut, but instead is passing in a value to the Master parameter on the OnMethodExitAdvice attribute. We’re defining the master advice method, which means we’re grouping the advices on the same “layer”. OnExit will be a slave method and will inherit the pointcut selectors from OnEntry since only master advice methods can define pointcuts. We only do this for advices of the same category of transformations. For example, you wouldn’t define OnEntry as the master advice method for OnPropertySet because they perform different transformations. We’ll cover grouping on another day.

The other methods in our aspect are the same, just using different advices. The OnInvoke method however has a different pointcut setup. We add an additional parameter, MemberName, giving it a value of “DoSomethingElse” which tells PostSharp to only apply the advice to methods matching “DoSomethingElse”.

Let’s run our example code and look at the result

class Program
{
    static void Main(string[] args)
    {
        ExampleClass ec = new ExampleClass();
        ec.DoSomething();
        ec.DoSomethingElse();
        ec.FirstName = "John";
        ec.LastName = "Smith";

        Console.ReadKey();
    }
}

[ComplexAspect]
public class ExampleClass
{
    public string FirstName { get; set; }
    public string LastName { get; set; }

    public void DoSomething()
    {
        Console.WriteLine("Doing something");            
    }

    public void DoSomethingElse()
    {
        Console.WriteLine("Doing something else");
    }

}

 
Entering DoSomething. MethodCounter = 1
Doing something
Exiting DoSomething. MethodCounter = 1
Before method DoSomethingElse. MethodCounter = 1
Entering DoSomethingElse. MethodCounter = 2
Doing something else
Exiting DoSomethingElse. MethodCounter = 2
After method DoSomethingElse. MethodCounter = 2
Entering set_FirstName. MethodCounter = 3
Setting property: FirstName = John. MethodCounter = 4
Exiting set_FirstName. MethodCounter = 4
Entering set_LastName. MethodCounter = 5
Setting property: LastName = Smith. MethodCounter = 6
Exiting set_LastName. MethodCounter = 6


In a single aspect, we have implemented advice and pointcuts that we normally would have written three different aspects using the base classes. In addition, we were able to share state between those advices. Examine the output of the MethodCounter. It’s incrementing as we continue along with the execution. If we had three individual aspects the provided equivalent advices, the output would look more like

Entering DoSomething. MethodCounter = 1
Doing something
Exiting DoSomething. MethodCounter = 1
Before method DoSomethingElse. MethodCounter = 0
Entering DoSomethingElse. MethodCounter = 1
Doing something else
Exiting DoSomethingElse. MethodCounter = 1
After method DoSomethingElse. MethodCounter = 0
Entering set_FirstName. MethodCounter = 1
Setting property: FirstName = John. MethodCounter = 1
Exiting set_FirstName. MethodCounter = 1
Entering set_LastName. MethodCounter = 1
Setting property: LastName = Smith. MethodCounter = 1
Exiting set_LastName. MethodCounter = 1

Advices

There are a few advice attributes that you can use. Each advice attribute has a corresponding simple aspect base class and behaves in the same way.

OnMethodEntryAdvice

OnMethodSuccessAdvice

OnMethodExceptionAdvice

OnMethodExitAdvice

These advices are the equivalent to the advices in the OnMethodBoundaryAspect base class

OnMethodInvokeAdvice

These advices are the equivalent to the advices in the MethodInterceptionAspect base class

OnLocationGetValueAdvice

OnLocationSetValueAdvice

These advices are the equivalent to the advices in the LocationInterceptionAspect base class

OnEventAddHandlerAdvice

OnEventRemoveHandlerAdvice

OnEventInvokeHandlerAdvice

These advices are the equivalent to the advices in the EventInterceptionAspect base class

IntroduceMember

Introduce a method, property or event to the target class.

IntroduceInterface

Introduce a method to the target class.

When applying advice to a method, the method must be public and have the same signature as the corresponding base class advice signature. For example, in order to apply OnLocationGetValueAdvice on a method, the method must be public and have a single LocationInterceptionArgs parameter with no return value (void).

Pointcuts

A pointcut has to be defined in order to tell PostSharp where to apply the advice. You can think of pointcuts as expressions that return a set of elements of code. These elements of code must be compatible with the type of advice (for instance, do not try to add an OnLocationGetValue advice to a field). Remember that you can only add advices to code that belong to the target of the aspect. So if the aspect has been applied to a type, you can only add advices to members of this type, or to the type itself.

MulticastPointcut

A declarative pointcut that works similarly to MulticastAttribute.

MethodPointcut

An imperative pointcut: the pointcut is a method that returns an enumeration of elements of code. The method can be implemented in C#, for instance using Linq.

SelfPointcut

A pointcut that evaluates to the target of the aspect.

MulticastPointcut

MemberName

MemberName takes an expression (static name, wildcard or regular expression) to specify targets.

Targets

Targets can be set to a combination of MulticastTargets flags. For example, MulticastTargets.Method | MulticastTargets.Property to specify the targets will be methods and properties.

Attributes

Attributes is how we define the scope and visibility of the intended targets and can be set to a combination of MulticastAttributes flags. For example, we can target members that are private and static by using MulticastAttributes.Private | MulticastAttributes.Static.

MethodPointcut

MethodPointcut allows us to pass in the name of a method that PostSharp can use to get a list of targets. Let’s look at an example

[Serializable]
public class ExampleAspect : TypeLevelAspect
{
    public IEnumerable FindTargetMethods(Type target)
    {
        IEnumerable targets = target.GetMethods()
                         .Where(c => c.Name.Contains("Something"));
        return targets;
    }

    [OnMethodEntryAdvice, MethodPointcut("FindTargetMethods")]
    public void OnEntry(MethodExecutionArgs args)
    {
        Console.WriteLine("Entering method: " + args.Method.Name);
    }

}

Our aspect has only one advice that we want to implement, OnMethodEntryAdvice. We use the MethodPointcut attribute and pass it “FindTargetMethods” which is the name of the method we’ve setup to determine which methods will be targets. The method that is going to return the targets has to have a specific signature. PostSharp documentation defines this signature as

IEnumerable SelectCodeElements(AspectTargetType target)

AdviceTargetType will be replaced with either object or a reflection type representing the targets of the advice. For example, MethodInfo when the advice targets are methods and PropertyInfo when the targets are Properties.

AspectTargetType will be replaced with either object or a reflection type corresponding to the targets of the aspect. For example, Type for AssemblyLevelAspect, TypeLevelAspect, InstanceLevelAspect and MethodInfo for MethodLevelAspect.

If the signature is not valid for the type of aspect then PostSharp will produce a compiler error. When we apply the aspect to our example class and run the application, we see that PostSharp has applied the advice to both of the class methods.

public class Program
{
    static void Main(string[] args)
    {
        ExampleClass ec = new ExampleClass();
        ec.DoSomething();
        ec.DoSomething1();

        Console.ReadKey();
    }
}

[ExampleAspect]
public class ExampleClass
{
    public int ID { get; set; }
    public string First { get; set; }
    public string Last { get; set; }

    public void DoSomething()
    {
        this.First = "John";
        this.Last = "Smith";
    }

    public void DoSomething1()
    {
        Console.WriteLine("Did something");
    }
}

The output is

Entering method: DoSomething
Entering method: DoSomething1
Did something


SelfPointcut

There is a special attribute that you can use instead of MulticastPointcut, the SelfPointcut attribute. SelfPointcut tells PostSharp to select the exact aspect target. For example, if we have the following aspect

[Serializable]
public class ExampleAspect: MethodLevelAspect
{
    [OnMethodEntryAdvice, SelfPointcut]
    public void OnEntry(MethodExecutionArgs args)
    {
        Console.WriteLine("Entering " + args.Method.Name);
    }

}

When applied to a method directly, the SelfPointcut will use that exact method as the target. If applied to a class, all methods in the class will get the advice because all of the methods would be the intended target. If the aspect was applied at the assembly level with specific targets configured, then PostSharp will use those exact targets. SelfPointcut is a way to defer the selection of pointcuts to a higher mechanism.

Benefits over base classes

Base classes encapsulate a single transformation, which means if you want to apply multiple transformations, you would need to build just as many aspects. For example, if you wanted to marshal a method invocation to a different thread and log exceptions that occur in that method, you would need to build two independent aspects and apply both to the target.

Sharing state between advices in multiple independent aspects requires convoluted mechanics. Building complex aspects using advices and pointcuts provides the benefit of sharing state between advices.

Conclusion

By now you should have a clear understanding of how to build complex aspects using the tools provided by PostSharp. The term “complex” shouldn’t be a deterrent because over the last two days we’ve seen just how easy it is to build aspects.

self573_thumb[1]Dustin Davis 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
Tutorial
12 July 2011

When it comes to building aspects, using the provided base classes such as OnMethodBoundaryAspect and LocationInterceptionAspect are quick and easy ways to implement a simple aspect. But they have their limitations: each aspect can only implement one transformation. But what if you need to encapsulate a design pattern made of several transformations? We’re going to look at two ways of building complex aspects: aspect providers today and advices tomorrow.

Base Aspect Classes

There are different base classes in which an aspect can derive from. For example, OnMethodBoundaryAspect is derived from MethodLevelAspect class because it deals with methods. These base classes are pre-configured for MulticastAttributeUsage and pre-implemented interfaces. These classes are just containers for behaviors, they do not implement any behavior themselves, but it’s important to choose the correct class when developing aspects.

· AssemblyLevelAspect – Base class for all aspects applied on assemblies

· TypeLevelAspect – Base class for all aspects applied on types

· MethodLevelAspect – Base class for all aspects applied on methods

· LocationLevelAspect – Base class for all aspects applied on fields, properties or parameters

· EventLevelAspect – Base class for all aspects applied on events

PostSharp does not rely on these classes, but on the interfaces they implement. You can create your own aspect classes by implementing the right interface – for instance IOnMethodBoundaryAspect.

IAspectProvider

When an aspect implements the IAspectProvider interface, it can provide additional aspects dynamically. Simply put, an aspect can tell PostSharp to apply other aspects to a target at compile time. Let’s take a look at an example

[Serializable]
public class Aspect1 : IOnMethodBoundaryAspect
{
    public void OnEntry(MethodExecutionArgs args)
    {
        Console.WriteLine("Aspect1: OnEntry for " + args.Method.Name);
    }

    public void OnException(MethodExecutionArgs args) { }
    public void OnExit(MethodExecutionArgs args) { }
    public void OnSuccess(MethodExecutionArgs args) { }
    public void RuntimeInitialize(MethodBase method) { }
}

[Serializable]
public class Aspect2 : IMethodInterceptionAspect
{
    public void OnInvoke(MethodInterceptionArgs args)
    {
        Console.WriteLine("Aspect2: OnInvoke for " + args.Method.Name);
        args.Proceed();
    }

    public void RuntimeInitialize(MethodBase method) { }
}

[Serializable]
public class ComplexAspect : MethodLevelAspect, IAspectProvider
{
    private readonly Aspect1 _aspect1 = new Aspect1();
    private readonly Aspect2 _aspect2 = new Aspect2();

    #region IAspectProvider Members

    public IEnumerable ProvideAspects(object targetElement)
    {
        MemberInfo nfo = (MemberInfo)targetElement;

        yield return new AspectInstance(targetElement, _aspect1);
            
        if (nfo.ReflectedType.IsPublic && !nfo.Name.Equals(".ctor"))
        {
            yield return new AspectInstance(targetElement, _aspect2);
        }
    }

    #endregion
}

We have three aspects here. Aspect1 and Aspect2 are just simple aspects that write their status to the console. The ComplexAspect however has some work happening. ComplexAspect derives from MethodLevelAspect because we’re going to be dealing with methods and it implements the IAspectProvider interface because we want to dynamically determine which aspects to apply to each target method.

We start by defining two instances one for Aspect1 and another for Aspect2. We’ll use these inside of ProvideAspects method when telling PostSharp to apply them to a target.

Inside the ProvideAspect method we cast targetElement as a MemberInfo structure. If we were using a LocationLevelAspect class instead of MethodLevelAspect class, we would cast it as a LocationInfo instead of MemberInfo because we’re working on a different level.

Since the return type of ProvideAspects method is an IEnumerable, we can use a yield return to provide aspects for PostSharp to apply. We return the instance of Aspect1 for all targets and then we check to see if the target method is public and is not a constructor. If it meets the criteria then we return the instance of Aspect2.

public class Program
{
    static void Main(string[] args)
    {
        ExampleClass ec = new ExampleClass();
        ec.DoSomething();

        Console.ReadKey();
    }
}

[ComplexAspect]
public class ExampleClass
{
    public void DoSomething()
    {
        Console.WriteLine("Did something");
    }
}

Our test code is a simple class with the ComplexAspect applied to it and we’re just making a call to DoSomething. The output is

Aspect1: OnEntry for .ctor
Aspect1: OnEntry for DoSomething
Aspect2: OnInvoke for DoSomething
Did something

Aspect1 was applied to both the constructor and the DoSomething method while Aspect2 was only applied to the DoSomething method.

CustomAttributeIntroductionAspect

PostSharp provides us with a very nice aspect that we can use to apply custom attributes to targets. For example, we can apply attributes to a DTO for XmlSerialization. Let’s look at an example of that

[AddXmlIgnoreAttribute]
public class ExampleClass
{
    [XmlElement]
    public int ID { get; set; }
    public string First { get; set; }
    public string Last { get; set; }
}

[MulticastAttributeUsage(MulticastTargets.Field | MulticastTargets.Property,
TargetMemberAttributes = MulticastAttributes.Public | MulticastAttributes.Instance)] public sealed class AddXmlIgnoreAttribute : LocationLevelAspect, IAspectProvider { private static readonly CustomAttributeIntroductionAspect
customAttributeIntroductionAspect = new CustomAttributeIntroductionAspect( new ObjectConstruction(typeof(XmlIgnoreAttribute)
.GetConstructor(Type.EmptyTypes))); public IEnumerable ProvideAspects(object targetElement) { LocationInfo memberInfo = (LocationInfo)targetElement; if (memberInfo.PropertyInfo.IsDefined(typeof(XmlElementAttribute), false) || memberInfo.PropertyInfo.IsDefined(typeof(XmlAttributeAttribute), false)) yield break; yield return new AspectInstance(memberInfo.PropertyInfo,
customAttributeIntroductionAspect); } }

We define a simple DTO class with four public properties. The DTO class is decorated with the AddXmlIgnoreAttribute.

Our aspect is going to apply the [XmlIgnore] attribute to all members of the DTO. If a member is already marked for inclusion using the [XmlElement] or [XmlAttribute] attributes then it will not receive the [XmlIgnore] attribute.

We start by defining the multicast options for our aspect. We tell PostSharp to only apply the aspect to fields or properties that are public and not static. Our aspect derives from LocationLevelAspect so we receive information structures specific to fields, properties and parameters.

We declare a new instance of CustomAttributeIntroductionAspect and passing in the ObjectConstruction data for XmlIgnoreAttribute type using the default empty constructor for it. We’ll cover how CustomAttributeIntroductionAspect works internally at another time.

In the ProvideAspects method, we cast targetElement as LocationInfo so we can work with the reflection info for the target. We use the LocationInfo to check if the property has either of the xml attributes defined on it and if it does we don’t yield any results for that target. Otherwise, we pass back to PostSharp the instance of CustomAttributeIntroductionAspect for application on the target.

Using ILSpy to see the end result

image

Except for ID, all of the properties now have XmlIgnore attributes.

Conclusion

IAspectProvider is a great way to dynamically determine which aspects to apply to a target at compile time but because aspects are still independent of each other, it has its limitations. Tomorrow we’re going to look at another way to build complex aspects.
self573_thumb[1]Dustin Davis 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
Tutorial
11 July 2011

Previously we’ve covered interception for methods and “locations” (fields/properties). Today we’re going to finish up interception by looking at the EventInterceptionAspect.

Intercepting Events

Events in .NET are similar to automatic properties. They look like fields, but you can override their underlying methods, Get and Set. Events don’t have Get or Set methods, instead they have Add and Remove methods. PostSharp allows us to intercept these Add and Remove methods as well as the invocation of the event using the EventInterceptionAspect base class. Let’s have a look

[Serializable]
public class EventAspect : EventInterceptionAspect
{
    public override void  OnAddHandler(EventInterceptionArgs args)
    {
        args.ProceedAddHandler();
        Console.WriteLine("Handler added");
    }

    public override void  OnRemoveHandler(EventInterceptionArgs args)
    {
        args.ProceedRemoveHandler();
        Console.WriteLine("Handler removed");
    }

    public override void OnInvokeHandler(EventInterceptionArgs args)
    {
        args.ProceedInvokeHandler();
        Console.WriteLine("Handler invoked");
    }
}

The aspect is very similar to the other aspects we’ve seen. We simply override the provided virtual methods for the actions we want to intercept - add, remove and invoke. Our test code is as follows

public class Program
{
    static void Main(string[] args)
    {
        ExampleClass c = new ExampleClass();
        c.SomeEvent += new EventHandler(c_SomeEvent);
        c.DoSomething();
        c.SomeEvent -= c_SomeEvent;

        Console.ReadKey();
    }

    static void c_SomeEvent(object sender, EventArgs e)
    {
        Console.WriteLine("Hello Event!");
    }
}

public class ExampleClass
{
    [EventAspect]
    public event EventHandler SomeEvent;

    public void DoSomething()
    {
        if (SomeEvent != null)
        {
            SomeEvent.Invoke(this, EventArgs.Empty);
        }
    }
}

The output is as expected

Handler added
Hello Event!
Handler invoked
Handler removed

EventInterceptionAspect

This aspect is pretty straight forward. We’re given three points in which we can intercept Add, Remove and Invoke. As with the other interception aspects, if one or more is applied to a target, the next node in the chain will be invoked and may not be the target event.

EventInterceptionAspect.OnAddHandler

Instead of the Add semantic of the event, the OnAddHandler is invoked instead. This occurs when a new handler is attached to the event (C# +=).

EventInterceptionAspect.OnRemoveHandler

Instead of the Remove semantic of the event, the OnRemoveHandler is invoked instead. This occurs when a delegate is removed from the event (C# -=).

EventInterceptionAspect.OnInvokeHandler

When the event is fired, the OnInvokeHandler method is invoked for each delegate attached to the event. If several handlers have been registered to the event, this method is called once for every handler.

EventInterceptionArgs

Each of the methods that we implement will have an EventInterceptionArgs parameter that we can use to get information and take action.

EventInterceptionArgs.Handler

Handler is the delegate that is currently being added, removed or invoked

EventInterceptionArgs.Instance

Instance is a reference to the instance from which the invocation is occurring, usually the class that the event is a member of. The instance where the invocation will occur can be changed by setting Instance to another class instance.

EventInterceptionArgs.Arguments

Arguments provides access to the arguments passed in during invocation of the event. For example, args.Argument[0] would typically be the value of the “sender” parameter and args.Arguments[1] would be the EventArgs (or some derivation).

EventInterceptionArgs.Event

Event is an instance of System.Reflection.EventInfo containing the reflected information for the target event. For more information on EventInfo, see the MSDN reference.

EventInterceptionArgs.AddHandler

AddHandler is the representation of the Add semantic of the event. You can call this directly to add a handler. It is possible to add additional/specific delegates to the event using AddHandler.

EventInterceptionArgs.ProceedAddHandler

Continues with the original request of adding a delegate to the target event.

EventInterceptionArgs.RemoveHandler

RemoveHandler is the representation of the Remove semantic of the event. You can call this directly to remove a handler. It is possible to remove additional/specific delegates to the event using RemoveHandler.

EventInterceptionArgs.ProceedRemoveHandler

Continues with the original request of removing a delegate from the target event.

EventInterceptionArgs.InvokeHandler

InvokeHandler allows us to invoke the handler, but it allows us to do so by providing a different delegate and arguments. InvokeHandler returns an object which will contain the return value of the delegate (if the delegate is not void).

EventInterceptionArgs.ProceedInvokeHandler

Continues with the original invocation of the delegate with the specified arguments. After invocation, args.ReturnValue may contain a value if the delegate is not void.

EventInterceptionArgs.ReturnValue

ReturnValue will contain the value returned by the delegate is it is not void. The return value can be changed or manipulated by setting ReturnValue to a new value.

Making Events Asynchronous

One of the uses for event interception is to invoke the registered delegates asynchronously. Let’s check out what that aspect looks like

[Serializable]
public sealed class AsyncEventAttribute : EventInterceptionAspect
{
    public override void OnInvokeHandler(EventInterceptionArgs args)
    {
        Task.Factory.StartNew(() => Invoke(args));
    }

    private static void Invoke(EventInterceptionArgs args)
    {
        try
        {
            args.ProceedInvokeHandler();
        }
        catch (Exception e)
        {
            args.ProceedRemoveHandler();
        }
    }
}

This is a very simple aspect. We are only implementing the OnInvokeHandler which has only one job, creating and starting a task. We use Task.Factory.StartNew() to create and immediately start the task asynchronously. We provide the StartNew method with an action which just makes a call to our Invoke method. Tasks are part of the Task Parallel Library which ships with .NET 4.0. If you are not familiar with the TPL or Tasks, please see the MSDN reference.

The Invoke method contains a try/catch structure. We make a call to args.ProceedInvokeHandler and if an exception occurs, we catch it and then remove that delegate from the event by calling args.ProceedRemoveHandler.

To test our aspect, we have modified our example from above.

public class Program
{
    static void Main(string[] args)
    {
        ExampleClass c = new ExampleClass();
        c.SomeEvent += new EventHandler(c_SomeEvent);
        c.SomeEvent += new EventHandler(c_SomeEvent);
        c.SomeEvent += new EventHandler(c_SomeEvent);
        c.SomeEvent += new EventHandler(c_SomeEvent2);
        c.SomeEvent += new EventHandler(c_SomeEvent2);
        c.SomeEvent += new EventHandler(c_SomeEvent2);

        c.DoSomething();

        Console.ReadKey();
    }

    static void c_SomeEvent(object sender, EventArgs e)
    {
        Console.WriteLine("Hello Event! Task: " + Task.CurrentId);
    }
    static void c_SomeEvent2(object sender, EventArgs e)
    {
        Console.WriteLine("Hello Event! Task: " + +Task.CurrentId);
    }
}

public class ExampleClass
{
    [AsyncEventAttribute]
    public event EventHandler SomeEvent;

    public void DoSomething()
    {
        if (SomeEvent != null)
        {
            SomeEvent.Invoke(this, EventArgs.Empty);
        }
    }
}

We register a few handlers with the event and then we call the DoSomething method that is going to invoke the event. Our output shows that the registered handlers were invoked and that they have been invoked in their own task.

Hello Event! Task: 1
Hello Event! Task: 2
Hello Event! Task: 3
Hello Event! Task: 4
Hello Event! Task: 5
Hello Event! Task: 6

Under the Hood

Just for fun, let’s open up ILSpy and look at our example code, you’ll see that there is a lot of work going on.

image

If you look around, you’ll notice a few things. Even though we didn’t implement OnAddHandler or OnRemoveHandler, PostSharp has modified the event to use <SomeEvent>_Broker to do the adding and removing of handlers.

<SomeEvent>_Broker is a nested class that PostSharp has added and it derives from EventBroker, an abstract class that is used to realize the interception of the invocation.

Notice that the DoSomething method doesn’t call our aspect’s OnInvokeHandler method, nor does it call to the <SomeEvent>_Broker. It simply does the invocation of the event just as it was coded in Visual Studio. How is it that it can intercept the invocation then? When <SomeEvent>_Broker is instantiated in the <>z__InitializeAspects method it gets a reference to our instance. EventBroker is a black box that uses our instance to wrap around the event. We’ll leave it at that for now.

Conclusion

The further we dig, the more we see just how comprehensive PostSharp really is. We still have more to cover. The topics are getting more complex, but I’ll try to make sense of them.
self573_thumb[1]Dustin Davis 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
Tutorial
8 July 2011

Continuing from yesterday, we’re covering the lifetime and scope of aspects. Today we’ll look at the scope of aspects.

Scope

Aspects come in two scopes, static and instance. Static scoped aspects are created and initialized at compile time for consumption at runtime using a singleton pattern. Instance scoped aspects are a bit different. Instanced scoped aspects use a prototype pattern by creating and initializing the aspect at compile time, but at run time when a new instance of a target member’s declaring type is created, a new instance of the aspect is created and used.

Static scoped aspects have the same lifetime as the application while instance scoped aspects have the same lifetime as the instance of the type the aspect was applied to.

However, no matter which scope an aspect will have, PostSharp creates an instance of the aspect for each target in which the aspect has been applied. No doubt this is all confusing so let's see what's going on with a demo.

Aspect demo

To make sense of all of this, let's start with a basic aspect.

[Serializable]
public class TestAspect : LocationInterceptionAspect
{
    private string InstID;
    private string aspectID;
    private string source;
    private int getCount = 0;

    public override void CompileTimeInitialize(LocationInfo targetLocation, 
                                                                 AspectInfo aspectInfo)
    {
        source = targetLocation.DeclaringType.Name + "." + targetLocation.Name 
                             + " (" + targetLocation.LocationType.Name + ")";
        aspectID = Guid.NewGuid().ToString();
    }

    public override void RuntimeInitialize(LocationInfo locationInfo)
    {
        InstID = Guid.NewGuid().ToString();
    }

    public override void OnGetValue(LocationInterceptionArgs args)
    {
        getCount++;

        Console.WriteLine(source + "\n\tInstance: " + this.InstID + "\n\tAspect: " +
                                             aspectID + "\n\tCount: " + getCount);
        args.ProceedGetValue();
    }
}

In CompileTimeInitialize we setup our source which is a combination of declaring type, target name and then the target's type. Then we assign a guid to aspectID which we'll use when we evaluate the aspect's life. In RunTimeInitialize we do the same thing and create a new guid that we use for the instance id. Then in the OnGetValue method we just increment the count and print all of our data to the console before calling ProceedGetvalue.

Our example class is as follows

class TestClass
{
    [TestAspect]
    public int MyField1;

    [TestAspect]
    public int MyField2;

    [TestAspect]
    public static int MyField3;

    public TestClass()
    {
        MyField1 = 1;
        MyField2 = 2;
    }

    static TestClass()
    {
        MyField3 = 10;
    }
}

And our test code

int val = 0;
for (int i = 1; i <= 2; i++)
{
    Console.WriteLine("--- PASS {0} ---", i);

    TestClass tc1 = new TestClass();
    TestClass tc2 = new TestClass();

    val = TestClass.MyField3;
                
    Console.WriteLine();
    val = tc1.MyField1;
    val = tc1.MyField2;

    Console.WriteLine();
    val = tc2.MyField1;
    val = tc2.MyField2;
                
    Console.WriteLine();
}

What we're doing is creating two instances of our TestClass and then getting the value of Myfield1 and MyField2 from each instance which our aspect will intercept and provide us with information about which aspect is handling our request.

Aspect for each target

When we run our test code, we get the following output (I’ve put the two passes side by side for comparison)

--- PASS 1 ---
TestClass.MyField3 (Int32)
Instance: d6c15b92-3ba1-4aba-8325-1ca07d50979c
Aspect: b73e55cf-6568-4b1c-9465-8d3b77f3288c
Count: 1

TestClass.MyField1 (Int32)
Instance: 667d5f03-ac25-43ef-86dd-7aa88ccc46c7
Aspect: a6e612ed-7ab9-4f31-956d-6be2f3198352
Count: 1

TestClass.MyField2 (Int32)
Instance: a5efc352-31f0-430c-8153-d78e0e1453c4
Aspect: 8feb7451-5df2-48e5-b57d-3588456aca96
Count: 1

TestClass.MyField1 (Int32)
Instance: 667d5f03-ac25-43ef-86dd-7aa88ccc46c7
Aspect: a6e612ed-7ab9-4f31-956d-6be2f3198352
Count: 2

TestClass.MyField2 (Int32)
Instance: a5efc352-31f0-430c-8153-d78e0e1453c4
Aspect: 8feb7451-5df2-48e5-b57d-3588456aca96
Count: 2
--- PASS 2 ---
TestClass.MyField3 (Int32)
Instance: d6c15b92-3ba1-4aba-8325-1ca07d50979c
Aspect: b73e55cf-6568-4b1c-9465-8d3b77f3288c
Count: 2

TestClass.MyField1 (Int32)
Instance: 667d5f03-ac25-43ef-86dd-7aa88ccc46c7
Aspect: a6e612ed-7ab9-4f31-956d-6be2f3198352
Count: 3

TestClass.MyField2 (Int32)
Instance: a5efc352-31f0-430c-8153-d78e0e1453c4
Aspect: 8feb7451-5df2-48e5-b57d-3588456aca96
Count: 3

TestClass.MyField1 (Int32)
Instance: 667d5f03-ac25-43ef-86dd-7aa88ccc46c7
Aspect: a6e612ed-7ab9-4f31-956d-6be2f3198352
Count: 4

TestClass.MyField2 (Int32)
Instance: a5efc352-31f0-430c-8153-d78e0e1453c4
Aspect: 8feb7451-5df2-48e5-b57d-3588456aca96
Count: 4

Let's break it down. In Pass 1 we see 5 calls made to our aspect. The first call is to the static member MyField3 while the next 2 are instance calls to MyField1 and MyField3 on tc1 instance and the same for the next 2 calls, but for the tc2 instance.

If you compare the aspect ID's, there are actually only 3 different instances of our aspect, one for each of the properties we applied it to, MyField1, MyField2 and MyField3 even though we have two separate instances of TestClass.

Static Scoped

Now that we've identified that a new aspect is generated for each target, let's examine the Instance ID's. In Pass 1 we see there are 3 instance ID's. Examining Pass 2, we see those exact same instance ID's even though we created new instances of our class.

Notice how the counter is increasing. In Pass 1 the first call to tc1.MyField1 results in 1 while the call to tc2.MyField1 results in a 2. In Pass 2 we see that the trend continues. This is due to the static nature of the aspect instances.

Instance Scoped

Aspects are only instance scoped when implementing the IInstanceScopedAspect interface or inheriting from InstanceLevelAspect, so let's update our aspect.

[Serializable]
public class TestAspect : LocationInterceptionAspect, IInstanceScopedAspect
{
    private string InstID;
    private string aspectID;
    private string source;
    private int getCount = 0;

    public override void CompileTimeInitialize(LocationInfo targetLocation, 
                                                                 AspectInfo aspectInfo)
    {
        source = targetLocation.DeclaringType.Name + "." + targetLocation.Name 
                      + " (" + targetLocation.LocationType.Name + ")";
        aspectID = Guid.NewGuid().ToString();
    }

    public override void RuntimeInitialize(LocationInfo locationInfo)
    {
        InstID = Guid.NewGuid().ToString();
    }

    public override void OnGetValue(LocationInterceptionArgs args)
    {
        getCount++;

        Console.WriteLine(source + "\n\tInstance: " + this.InstID 
                 + "\n\tAspect: " + aspectID + "\n\tCount: " + getCount);
        args.ProceedGetValue();
    }


    #region IInstanceScopedAspect Members

    public object CreateInstance(AdviceArgs adviceArgs)
    {
        return this.MemberwiseClone();
    }

    public void RuntimeInitializeInstance()
    {
        InstID = Guid.NewGuid().ToString();
    }

    #endregion
}

Now we implement IInstanceScopedAspect which requires CreateInstance() and RuntimeInitializeInstance(). CreateInstance is called to create a new instance of the aspect based on the current instance, thus using the current instance as a protoype. All we need to do is use the MemberwiseClone() and we're set.

RuntimeInitializeInstance is where we update our instance ID. If we didn't, we would only get the instance ID specified in the RuntimeInitialize which is only invoked once for each aspect when it's deserialized, not when a new instance is created.

Let's run our test code and see how things have changed. (I’ve put the two passes side by side for comparison)

--- PASS 1 ---
TestClass.MyField3 (Int32)
Instance: ea470da4-ed8d-49e1-be96-5ae16ac000a6
Aspect: b6aa3c5c-3868-40e8-af37-70234032d734
Count: 1

TestClass.MyField1 (Int32)
Instance: a0b96c14-0d81-463f-9374-6915b6def2a0
Aspect: a975c1ce-6839-454c-b054-14b185fd29a8
Count: 1

TestClass.MyField2 (Int32)
Instance: 3a6676c6-0325-4ee3-8dbb-fe4d6c93acdd
Aspect: dbd6865f-3b5b-48dd-a87e-ef8a89695e2f
Count: 1

TestClass.MyField1 (Int32)
Instance: 3ee8689a-a1a9-4dfc-9ca6-10bd50129a1d
Aspect: a975c1ce-6839-454c-b054-14b185fd29a8
Count: 1

TestClass.MyField2 (Int32)
Instance: 84f63d43-c91a-46ba-8bda-89ad16d0ba2c
Aspect: dbd6865f-3b5b-48dd-a87e-ef8a89695e2f
Count: 1
--- PASS 2 ---
TestClass.MyField3 (Int32)
Instance: ea470da4-ed8d-49e1-be96-5ae16ac000a6
Aspect: b6aa3c5c-3868-40e8-af37-70234032d734
Count: 2

TestClass.MyField1 (Int32)
Instance: 2ad257df-682e-4652-98fa-5466d2bb08aa
Aspect: a975c1ce-6839-454c-b054-14b185fd29a8
Count: 1

TestClass.MyField2 (Int32)
Instance: e39fe1e1-ca7a-4b26-90ac-8c81fc1aafe5
Aspect: dbd6865f-3b5b-48dd-a87e-ef8a89695e2f
Count: 1

TestClass.MyField1 (Int32)
Instance: cb40067e-2b69-4c5b-814e-4a7420a085a9
Aspect: a975c1ce-6839-454c-b054-14b185fd29a8
Count: 1

TestClass.MyField2 (Int32)
Instance: e4c21efc-92e6-41cc-bf34-babbddf4ea48
Aspect: dbd6865f-3b5b-48dd-a87e-ef8a89695e2f
Count: 1

First, have a look at the Aspect ID's. Notice that again, there are only 3 ID's as we only have 3 targets. They are used both in Pass 1 and Pass 2. This is the same as the static scoped example. The difference is the instance ID's. Except for TestClass.Myfield3, which is static, there are no repeating instance ID's. For each target, we get a new instance of the aspect when we instantiated a new instance of TestClass. This can be verified by examining the count. Since we only make one get call per TestClass instance, count is always 1 because it's scoped to the current instance of the declaring type, tc1 and tc2.

So what happened with TestClass.MyField3? Even though the aspect implements IInstanceScopedAspect, when applied to a static target, the aspect instance becomes static scoped. This only makes sense considering the nature of static members.

Conclusion

It's amazing how much flexibility PostSharp gives us, but as I've stated before, a solid understanding of how it works is key to producing quality results. This week we spent a lot of time under the hood looking at what PostSharp does when you click the build button.

 

self573_thumb[1]Dustin Davis 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
Tutorial
7 July 2011

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
Tutorial
6 July 2011

Download demo project source code

Today we jump face first into the decompiled code of interception aspects. If you have not yet read the previous post (part 1), then I suggest doing so before continuing. We’re going to look at the IoCResolution aspect we built yesterday and finish off with chaining.

Under the hood

Using ILSpy, open the PostSharpDemo1.exe and browse to the contactRepository field under ContactManager.

image

We see a getter and a setter even though this was a field and not a property. The setter contains the standard code to set the backing to the value passed in. There is no change since we did not implement OnSetValue in our aspect.

The getter starts off by instantiating a generic instance of LocationInterceptionArgs that will be passed to our aspect’s OnGetValue method. Next it sets the Location property to the LocationInfo held in <>z_Aspects.l7. We saw this in the MethodInterceptionAspect except it was a MethodInfo type instead of LocationInfo. <>z_Aspects.l7 is specifically for the ContactManager.contactRepository field. TypeBinding is then set to the singleton instance of the binding PostSharp has created for contactRepository, ContactManager.<contactRepository>c_Binding.

The TypeBinding is necessary because the LocationInterceptionArgs contain the ProceedSetValue, ProceedGetValue, GetCurrentValue and SetNewValue methods which use the binding to access the field. We use these methods to manipulate the field/property from our aspect.

image

Finally, our aspect’s OnGetValue method is called passing in the LocationInterceptionArgs and then the value to returned using the LocationInterceptionArgs.TypedValue. TypedValue will always be of the generic type used to instantiate LocationInterceptionArgs.

Chaining

When it comes to interception aspects, we have to remember how they work because when applying multiple interception aspects to the same target, a chain is created. Each interception aspect is a node on the invocation chain which means you may or may not be invoking the actual target when proceeding. If we were to apply both a location interception aspect and a method interception aspect to a single target, the chain would become

image

Each aspect intercepts the preceding aspect. The original call ends up invoking the last applied aspect. It’s important to understand this when applying aspects that change the invocation as many things can be affected such as performance and behavior.

Note that the ordering of the MethodInterceptionAspect and the LocationInterceptionAspect may be inversed. We’ll see another day how to specify the order of application of several aspects when they are applied to the same element of code.

Conclusion

It may seem overwhelming at first, but once you understand how PostSharp produces its results it’s easy to grasp what’s going on when troubleshooting. Thankfully PostSharp is a mature framework and there are little, if any, side effects.

 

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
Tutorial
5 July 2011

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
Tutorial
4 July 2011

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