Click here to Skip to main content
Email Password   helpLost your password?

Introduction

This is the second article in a series of two, with this one going into the details of the real-world implementation of custom attributes that actually add behaviors to your code, namely performance instrumentation and field validation.

The first article of this series introduced some key concepts of PostSharp, based on a simple example: a tracing custom attribute derived from OnMethodBoundaryAspect. This article presents two more types of custom attributes, with two examples: a performance counter based on OnMethodInvocationAspect, and a field validation framework based on OnFieldAccessAspect.

The Two Lives of a Custom Attribute

Before jumping into the implementation of new custom attributes, let's delve into the internal workings of PostSharp Laos. PostSharp is an MSIL enhancer: it inserts itself in the build process, and modifies the output of the compiler (C#, VB.NET, J#, …). It looks for PostSharp Laos custom attributes, and modifies the methods, types, and fields to which they are applied.

To derive the maximum advantage from PostSharp Laos, it is necessary to fully understand the lifecycle of its custom attributes. They really have two lives: a first one at compile-time, inside PostSharp; and a second one at runtime.

The life cycle of a PostSharp Laos custom attribute is as follows:

  1. For each application of the custom attribute, a new instance is created. Therefore, an instance of a custom attribute is always assigned to one and only one method, field, or type. Then, instances are initialized (method CompileTimeInitialize) and validated (method CompileTimeValidate).
  2. Custom attributes are serialized to a binary blob.
  3. They are stored in a managed resource of the output assembly.
  1. Custom attributes are deserialized from the managed resource, and each instance is initialized a second time (method RuntimeInitialize),
  2. Runtime methods (OnEntry, OnExit, …) are invoked as the method or field to which it is applied is invoked or accessed.

Okay, that's enough about the theory for now. Let's move on to the code!

Performance-Counter Custom Attribute

How do you monitor the performance of an application in a production environment where you cannot use your favorite profiler? Answer: you can use performance counters, but it requires a lot of plumbing code to instrument existing code. The solution is to encapsulate this plumbing code as an aspect, i.e., to make a custom attribute out of it, and then apply this custom attribute to every relevant method.

And what if we want to instrument a method located outside the current assembly? It's not a problem for PostSharp to apply a custom attribute to external declarations. However, since we can't modify the method, we have to intercept its call. So, instead of the OnMethodBoundary aspect, we will use the OnMethodInvocation:

[Serializable]
public class PerformanceCounterAttribute : OnMethodInvocationAspect
{

    public override void OnInvocation( MethodInvocationEventArgs eventArgs )
    {
       // Our implementation goes here.
    }
}

In the eventArgs parameter, we get information about the method actually being called: eventArgs.Delegate is a delegate to the intercepted method, and eventArgs.GetArguments() gives the arguments passed to the method. PostSharp Laos expects us to put the return value in eventArgs.ReturnValue. Therefore, we can call the intercepted method as follows:

eventArgs.ReturnValue = eventArgs.Delegate.DynamicInvoke(
                                             eventArgs.GetArguments() );

1. Measuring Performance

Our performance counter should count the number of invocations and should measure the time spent in the method. Since every instance of the custom attribute is associated with one and only one intercepted method, we can store the hit count and the elapsed time as instance fields of the custom attribute.

Based on these principles, here is our first working implementation:

[Serializable]
public class PerformanceCounterAttribute : OnMethodInvocationAspect
{
    private long elapsedTicks;
    private long hits;

    public override void OnInvocation( MethodInvocationEventArgs eventArgs )
    {
        Stopwatch stopwatch = Stopwatch.StartNew();
         
        try
        {
            
            eventArgs.ReturnValue = eventArgs.Delegate.DynamicInvoke(
                                                  eventArgs.GetArguments() );
        }
        finally
        {
            stopwatch.Stop();
            Interlocked.Add( ref this.elapsedTicks, stopwatch.ElapsedTicks );
            Interlocked.Increment( ref this.hits );
        }
    }
}

2. Discovering Performance Counter Instances

It works, but how do we read the values? We simply have to expose fields in read-only public properties and build a repository of performance counter instances. We have to build this repository at runtime; therefore, we cannot do this registration in the instance constructor (which is called at compile-time), but must do it in the RuntimeInitialize method. One last thing: we have to expose the identity of the instrumented method; otherwise, how would we know to which method the performance counter relates? So, we must store the target method in a field and expose it in a read-only property. The RuntimeInitialize method is the right place to initialize this field.

Here is the code we have to add to the class:

[NonSerialized] private MethodBase method;

private static readonly List<performancecounterattribute /> instances =
              new List<performancecounterattribute />();

public override void RuntimeInitialize( MethodBase method )
{
   base.RuntimeInitialize( method );
   this.method = method;
   instances.Add( this );
}
public MethodBase Method { get { return this.method; } }

public double ElapsedMilliseconds 
{ 
  get { return this.elapsedTicks/( Stopwatch.Frequency/1000d ); } 
}

public long Hits { get { return this.hits; } }

public static ICollection<performancecounterattribute /> Instances 
{ 
  get 
  { 
    return new ReadOnlyCollection<performancecounterattribute />( instances ); 
  } 
}

3. Done. Use it.

And that's all! We can now apply our custom attribute to the methods we want to instrument. Suppose we want to measure the time spent in the System.IO namespace. We will add a performance counter to these methods, using the following piece of code:

[assembly: PerformanceCounter(AttributeTargetAssemblies = "mscorlib", 
           AttributeTargetTypes = "System.IO.*")]

And, if we instrument a small program listing the content of a folder, we get the following output:

One of the things you should be aware of is that this aspect intercepts only calls made from the current assembly. So, if you call an external method that indirectly calls an instrumented method, this indirect call won't be monitored. This limitation is inherent to the technology used by PostSharp: MSIL rewriting.

Validating Fields With Custom Attributes

So far, we've seen how to modify method bodies and how to intercept method calls. PostSharp makes it possible to intercept "get" and "set" operations on fields. One of the applications of this technique involves the validation of fields: we could make a field non-nullable or enforce a regular expression just by decorating the field with a custom attribute.

Aspects that need to intercept field accesses should derive from the OnFieldAccessAspect class. They may override the OnGetValue() and OnSetValue() methods. For field validation, we are only interested in the second method. All we have to do is perform the validation that is specific to the validator.

1. Designing an Abstract Framework

The class design of a validation framework is simple: we have basically an abstract class FieldValidationAttribute exposing an abstract method Validate() called from the overridden OnSetValue() method. By contract, the implementation of Validate() should throw an ad-hoc exception if the method is invalid. The exception will typically include the field name. So, it would be fine for the FieldValidationAttribute class to expose the name of the field to which the custom attribute instance is applied. Since this information is known at compile-time, it is initialized in the CompileTimeInitialize() method and stored in a serializable field.

One thing we have to remember is that, just like OnMethodInvocationAspect, OnFieldAccessAspect intercepts accesses to a field; it is therefore limited to the current assembly. If you follow Microsoft's recommendation and have only private fields, this is not a problem. But if you prefer to have public fields, you should ask PostSharp Laos to encapsulate fields into properties. Just override the GetOptions() method and return GenerateProperty.

Here is the complete code for the FieldValidationAttribute class:

[Serializable]
[AttributeUsage( AttributeTargets.Field, AllowMultiple = false )]
public abstract class FieldValidationAttribute : OnFieldAccessAspect
{
    private string fieldName;

    public override void CompileTimeInitialize( FieldInfo field )
    {
        base.CompileTimeInitialize( field );

        this.fieldName = field.DeclaringType.Name + "." + field.Name;
    }

    public string FieldName { get { return this.fieldName; } }

    protected abstract void Validate( object value );

    public override sealed void OnSetValue( FieldAccessEventArgs eventArgs )
    {
        this.Validate( eventArgs.ExposedFieldValue );

        base.OnSetValue( eventArgs );
    }

    public override OnFieldAccessAspectOptions GetOptions()  
    {
        return OnFieldAccessAspectOptions.GenerateProperty;
    }

}

2. Checking Non-Null Fields

The "non-nullable" field aspect is the most trivial:

[Serializable]
public sealed class FieldNotNullAttribute : FieldValidationAttribute
{
    protected override void Validate( object value )
    {
        if ( value == null )
            throw new ArgumentNullException( "field " + this.FieldName );
    }
}

Defining a non-nullable field is as easy as this:

class MyClass
{
  [FieldNotNull] 
  public string Name = "DefaultName";
}

3. Checking with Regular Expressions

A more challenging case is to design a custom attribute that checks regular expressions. The constructor of the custom attribute should accept the matching pattern as a parameter, as well as, optionally, a value indicating whether null values are acceptable for this field. If we want to avoid having to recompile the regular expression at each field assignment, we can store it as an instance field that we should initialize at runtime, in the RuntimeInitialize() method.

Here is a basic but working implementation of the pattern-matching field validator:

[Serializable]
public sealed class FieldRegexAttribute : FieldValidationAttribute
{
    private readonly string pattern;
    private readonly bool nullable;
    private RegexOptions regexOptions = RegexOptions.Compiled;

    [NonSerialized]
    private Regex regex;

    public FieldRegexAttribute(string pattern, bool nullable)
    {
        this.pattern = pattern;
        this.nullable = nullable;
    }

    public FieldRegexAttribute(string pattern) : this(pattern, false)
    {
    }

    public RegexOptions RegexOptions
    {
        get { return regexOptions; } 
        set { regexOptions = value; }
    }

    public override void RuntimeInitialize(FieldInfo field)
    {
        base.RuntimeInitialize(field);
        this.regex = new Regex( this.pattern, this.regexOptions);
    }

    protected override void Validate( object value )
    {
        if ( value == null )
        {
            if ( !nullable )
            {
                throw new ArgumentNullException("field " + this.FieldName);
            }
        }
        else
        {
            string str = (string) value;
            if ( !this.regex.IsMatch( str ))
            {
                throw new ArgumentException( 
                    "The value does not match the expected pattern.");
            }
        }
    }
}

4. Done. Use it.

We're done! We have developed custom attributes that allow us to validate fields at runtime.

Their use is very straightforward:

class MyClass
{
    [FieldNotNull] 
    public string Name = "DefaultName";
    
    [FieldRegex(@"^([\w-\.]+)@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.)|
                 (([\w-]+\.)+))([a-zA-Z]{2,4}|[0-9]{1,3})(\]?)$")] 
    public string EmailAddress;
}

Look at the resulting assembly, using Lutz Roeder's Reflector:

Fields have been encapsulated into properties, and if you look at the implementation of accessors, you'll see that our validating custom attributes have been invoked.

Simple. Powerful. What more could you want?

Summary

While the first article of this series introduced most of the key concepts of PostSharp Laos using a simple example based on OnMethodBoundaryAspect, the current article introduced two more aspects: OnMethodInvocation and OnFieldAccess.

We have seen the difference between OnMethodBoundaryAspect and OnMethodInvocationAspect: whereas the first actually adds a try-catch block on the target method, the second one intercepts method calls and does not modify the target method. This makes it possible to apply OnMethodInvocationAspect even on methods defined outside the current assembly. The first example exploited this feature to measure the time spent in the namespace System.IO.

The second example illustrated how to add behaviors to field behaviors. We have also seen how to generate a property around a field so that the behavior is also invoked from assemblies other than the current one.

Principally, I hope that you are now convinced that we can revisit the way we currently solve cross-cutting problems: Aspect-Oriented Programming is an elegant solution to most of these, and PostSharp Laos provides a simple and powerful technology.

Now, look at the three last projects you've worked on, and think about how much effort you could have saved with PostSharp…

You must Sign In to use this message board.
 
 
Per page   
 FirstPrevNext
QuestionCalling advised methods from outside of assembly
jnoody
9:07 3 Apr '09  
"One of the things you should be aware of is that this aspect intercepts only calls made from the current assembly. So, if you call an external method that indirectly calls an instrumented method, this indirect call won't be monitored. This limitation is inherent to the technology used by PostSharp: MSIL rewriting."

Is this only related to the use of the following code?

[assembly: PerformanceCounter(AttributeTargetAssemblies = "mscorlib", 
AttributeTargetTypes = "System.IO.*")]


Or does this statement apply even when applying a OnMethodBoundaryAspect or OnMethodInvocationAspect attribute to a method?

Thanks.
QuestionIssue with 3.5 and assembly entries
alreadyused
6:40 9 Mar '09  
I am running C# 3.5 and am having issues with entries at the assembly level. I have rolled the build back to 2.0 and 3.0 as well, and no luck.

In the first example, Logging , one of the two entries work.
This one works: [assembly: Trace("Logging")] with [Trace(null, AttributeExclude = true)] on TraceAttribute
and this one doesn't: [assembly: Trace("Logging", AttributeTargetTypes = "say*")]

In the second example, PerformanceCounter, the entry is:
[assembly: PerformanceCounter(AttributeTargetAssemblies = "mscorlib", AttributeTargetTypes = "System.IO.*")]

The results in both cases that fail are that the code compiles, but the overrides never happen. So it seems that AttributeTargetTypes may be incompatible with 3.5.
On the logging example, I tried replacing say* with sayHello (and verified the case is correct) but still no luck.

Has anyone else run into this, or do you have any ideas?
AnswerRe: Issue with 3.5 and assembly entries
Gael Fraiteur
6:45 9 Mar '09  
For questions related to PostSharp, please use http://www.postsharp.org/forum/. Thank you.

Gael Fraiteur
Lead developer of PostSharp, a platform for AOP on .NET.

QuestionRuntime Instrumentation
Hendry Luk
18:18 28 Aug '08  
Gael, this is a great article.
Im wondering if its possible to instrument a compiled assembly at runtime? I.e., without compile steps, for instance for external 3rd party components.
AnswerRe: Runtime Instrumentation
Gael Fraiteur
22:02 28 Aug '08  
Hendry,

I haved replied here:

http://www.postsharp.org/blog/2008/08/using-postsharp-at-runtime/[^]

-gael

Gael Fraiteur
Lead developer of PostSharp, a platform for AOP on .NET.

GeneralFantastic & Cacheable function attribute
Tom Janssens
4:40 7 Dec '07  
Hey Gael,
First of all, I think you delivered an EXCELLENT library.

I just discovered this a few hours ago, and I allready wrote a small but very helpfull attribute : a attribute to cache constant but timeconsuming function calls.
If you have a function that takes a lot of time to generate, but the result set is actually the same each time for a certain parameter set, you simply need to define it as follows :
(code is written in VB since we're using vb @ work here)

Module Module1
Sub Main()
Console.WriteLine("calling Div2 for i = 1", DoDiv2(1))
Console.WriteLine("calling Div2 for i = 2", DoDiv2(2))
Console.WriteLine("calling Div2 for i = 1", DoDiv2(1))
Console.ReadLine()
End Sub


<Cacheable()> _
Function DoDiv2(ByVal i As Integer) As Double
Console.WriteLine("running Dodiv2 for " + i.ToString())
Return i / 2
End Function
End Module


If you run this code, you will see that the function only gets executed once for parameter value 1; the second time it just searches the cached dictionary. This avoids having the same code all over the project caching calculated values !!! It is a huge timesaver and makes you code much more maintainable...

The attribute itself was written like this :
Imports ps = PostSharp.Laos

<Serializable()> _
<System.AttributeUsage(AttributeTargets.Method, Inherited:=True, AllowMultiple:=False)> _
Public Class CacheableAttribute
Inherits ps.OnMethodInvocationAspect

Shared x As New Hashtable

Public Overrides Sub OnInvocation(ByVal pEventArgs As PostSharp.Laos.MethodInvocationEventArgs)
Dim o() As Object = pEventArgs.GetArguments()
Dim i As Integer = 0, obj As Object
For Each obj In o
i += obj.GetHashCode()
Next
If Not x.Contains(i) Then
x(i) = pEventArgs.Delegate.DynamicInvoke(o)
End If
pEventArgs.ReturnValue = x(i)
End Sub
End Class


And this in such a short time period. Thanks a lot !!!
BTW(Youve got my 5 on both articles)

Tom

Core


GeneralRe: Fantastic & Cacheable function attribute
Gael Fraiteur
12:34 14 Dec '07  
Thank you, Tom!

There's indeed a lot of applications to PostSharp and these articles's goal was only to give taste to continue....

Note that PostSharp installs some samples including... a caching custom attribute.

Also, the project http://code.google.com/p/postsharp-user-samples/[^] collects samples submitted by users.

Feel free to submit your own!

Gael
Gael Fraiteur
Lead developer of PostSharp, a platform for AOP on .NET.

GeneralWow -- this is great
Tom Hawkins
5:43 12 Oct '07  
Thanks so much for your article -- went to your website also and I'm very excited about the possibilities.

But, and this is a lazy question, at a conceptual level what is the fundamental trickery by which this AOP framework is implemented (so I don't have to read through and analyze the code)?

This is an important question because if my company stakes functionality on this framework (which isn't so widespread and doesn't yet have a vast, committed community of open source users and consultants), and then you get hit by a bus, I'd like to have a comfort level that, yes, we could extend it / migrate it to future releases of the .NET platform on our own with an acceptable amount of effort.

Can you please give me a 1 paragraph conceptual summary of how it works (more if you like)?

Thanks in advance,
Tom
GeneralRe: Wow -- this is great
Gael Fraiteur
6:04 12 Oct '07  
Tom,

The principle is that the compiled assembly (the output of the C# compiler) is parsed into an object model, then it goes through a pipeline of analysis and transformations. The last task of the pipeline is to write back the assembly. PostSharp uses ILASM as a backend to rewrite the assembly.

It is important to understand that there is absolutely no trick. PostSharp generates valid (and verifiably type-safe) .NET assemblies, that any implementation of the ECMA specification can run. Last two weeks, I have ported PostSharp to Mono, and PostSharp can now target the Compact Framework.

PostSharp has to be upgraded when a new version of the *ECMA specification* is released, not when the CLR implementation is updated and, most importantly, not everytime Microsoft adds new features to their compiler or add new class libraries. That's why PostSharp's compatibility with .NET 3.0 and 3.5 were litterally effortless. I don't know about an undergoing project at Microsoft to add features to the ECMA spec.

As for support, it's true the community is currently small (there are hundreds of passive members, i.e. members asking questions, but I am the only one really answering). I hope I will be able to negociate some partnership with a bigger company to provide a better umbrella. That will be work for next year!

I hope it answers your questions.

Gael

Gael Fraiteur
Lead developer of PostSharp, a platform for AOP on .NET.

GeneralRe: Wow -- this is great
Tom Hawkins
6:14 12 Oct '07  
Thanks very Gael, I appreciate your prompt response.

May all your work be rewarded Smile

Tom
QuestionWhat about properties validation?
Eduard Gomolyako
3:29 21 Sep '07  
Okay, now I can to decorate fields of my class with validation attributes, but what about properties?

Best, Ed.

AnswerRe: What about properties validation?
Gael Fraiteur
3:39 21 Sep '07  
You would need to create an OnMethodBoundaryAspect aspect, apply it on the set accessor, and get the value with eventArgs.GetArguments()[0].

Unfortunately, there is no elegant way with Laos to build a CA that would apply both on fields and properties.



Gael Fraiteur
Lead developer of PostSharp, a platform for AOP on .NET.

GeneralGood Article...But
P.Adityanand
5:31 20 Sep '07  
Hi Gael,

Good Article. But, why would I use some thrid party AOP tool to tweak my code? This may be useful for some defect injection or logging during testing phase. Otherwise I would put code that best suites my assembly behavior and that would achieve my performance, security and design goals.

I don't see AOP being used in real production environments where a complete control of the code is required for several reasons, like performance, security, standard design patterns, etc.

Anyway this is my personal opinion based on my experience.

Regards,
Aditya.P
GeneralRe: Good Article...But
Gael Fraiteur
5:47 20 Sep '07  
Hi Aditya,

If you use post-compilation techniques, you have complete control of your code since you can actually see (using a decompiler) what you deliver. There is no secret, so hidden calls to profiling APIes, no burden of remoting proxies, ...

AOP is quite new in the .NET community, but this technology is already 10 years old. In the Java community, AOP is very common. The reference implementation, AspectJ, also uses bytecode postprocessing. There has been extensive researchs and there is a large industry experience with AOP so one can say it is... well, relatively mature, of course not so mature than OOP!

You mentioned design patterns, but AOP is precisely one of the ways to encapsulate design patterns to make them apply automatically (where possible, of course -- I did not say that design patterns were obsolete !!).

This article was very fun oriented, but I'm convinced they are serious reasons to think AOP for crosscutting concerns.

- Gael

Gael Fraiteur
Lead developer of PostSharp, a platform for AOP on .NET.

GeneralRe: Good Article...But
mcory2
6:38 20 Sep '07  
I can think of one use right off the bat, even based on the stuff in this article. (I'm not terribly fluent in or familiar with most AOP concepts, so I'll probably explain this kinda wrong.)

Create an "AOP-based" profiling library. When you create your production build, use the post compiler to create an additional profiling build off that original code base and test with that. That way you don't need to litter your source code with conditional compilation statements and worry about forgetting to turn it on or off, you can test the actual code you're shipping (to some degree -- you can claim that it isn't the same code, as it's changed after compilation), and you don't have to rebuild to get your "real" production build to ship.

Also, perhaps you're shipping multiple versions of the same product -- perhaps custom features for specific customers. Depending on the situation, you can get away with leaving your original code base intact, and use AOP to add the necessary, additional features.

An example: say your normal product allows all users to load widgets (calling the "LoadWidget" method, of course). One customer only wants admins to be able to load widgets. Instead of modifying the original code base and either branching or worrying about breaking functionality for several other customers, you can use AOP to add a CheckUser method call to execute immediately prior to LoadWidget.

You still have the source code for both, and when the time comes that more users want that functionality, you can consider implementing it in the main release; until then though, using AOP might allow for better stability.

Personally, I don't know enough about AOP to say that I truly would use it in a product, but I can definitely see that there's tons of possibilities with it if I'd let my imagination have a little fun with it and break out of the standard (and rather rigid) development practices I've made for myself.
GeneralRe: Good Article...But
asthalas
21:20 24 Sep '07  
I guess the use you make of AOP is directly proportional on how well you can abstract the different points of view in your project. Personally, I've used AOP (implemente through custom attributes and with some help from Reflection) for controlling security (method access rights and different behaviour depending on user role) and persistence.
That last one is the one I'm most proud of, since a simple tag does all the work of generating the SQL to retrieve/save objects. And performing searches.
So I guess it's all up to your imagination.

Personally, I LOVE AOP.


Last Updated 20 Sep 2007 | Advertise | Privacy | Terms of Use | Copyright © CodeProject, 1999-2010