Aspects for .NET

Aspect Oriented Programming Support for .NET


Aspects for .NET is a library that you can use to add aspect orientation to your .NET application. It provides interfaces that you can implement to build your own aspects which you can then apply to your implementation classes. It also then provides a mechanism to wrap, at runtime, your implementation with aspects (and no, it does not mess with the MSIL post-compilation). There is also integrated support for MEF, albeit with some limitations.
In A Nutshell
Say you have an aspect attribute MyAspectAttribute applied to an implementation MyImplementation that implements the contract IMyContract:

public interface IMyContract
{
    void DoThing();
}

public class MyImplementation : IMyContract
{
    [MyAspect]
    public void DoThing()
    {
        // ... Do stuff
    }
}
Then, you can get an "aspectized" instance like so:

var myInstance = AspectHelper.Wrap<IMyContract>(new MyImplementation());
myInstance.DoThing();
Installation
Download the NuGet package Aspects and add to your application:

Install-Package Aspects
How It Works
When you call Wrap, it inspects the contract interface and implementation class using reflection. It uses CodeDOM to generate an on-the-fly implementation of the contract interface, which expects an instance of the implementation to be initialized, and where each member simply calls out to the implementation (i.e. a hollow wrapper). However, in the process, it also inspects the implementation member to see if any aspect attributes are applied and adds code at appropriate places before or after the invocation to execute those aspects. The type that is generated on the fly is cached in memory.

There are also overloads of the Wrap method in the AspectHelper class that support lazy instantiation, as well as a corresponding set of WrapMany methods that deal with multiple instances. In addition, two events CodeGenerated and CodeCompiled are provided that you can handle to inspect or change the generated output.
MEF Support
Going back to the previous example, if MyImplementation is also a MEF export for IMyContract, like so:

[Export(typeof(IMyContract))]
public class MyImplementation { .... }
And you're MEF-importing IMyContract like so:

[Import] IMyContract myContract
You can easily inject aspects by following these steps:
  1. In your startup routine, after you've composed your MEF container (say myContainer), call the following:

    AspectHelper.RegisterForComposition(myContainer, 
    	/* list of assemblies to scan for MEF exports */);
  2. Modify your import declaration as:

    [Import(typeof(IAspected<IMyContract>))] IMyContract myContract

There are a few limitations though:
  • You must use an AggregateCatalog to compose the container.
  • Exports with multiple cardinalities are not supported (i.e. does not work with ImportMany).
  • Support for derivations of Export (e.g. InheritedExport) has not been tested.
  • Support for metadata attributes has not been tested.
Creating Aspects
Aspects can be applied to methods or properties by decorating the methods or properties with the aspect attribute class. You can also decorate the class - which will apply the aspect to all properties or methods within the class that belong to the contract. If multiple aspects of the same type are applied, they are executed in order as defined by the Order property. Three types of aspects are supported:

Entry Aspect: Executed as soon as control enters into the method or property. To create an entry aspect, create an attribute class (i.e. one that inherits from Attribute) that implements IEntryAspect.

public class MyEntryAspectAttribute : Attribute, IEntryAspect
{
    public bool Execute(MemberInfo memberInfo, IDictionary<string, object> parameters)
    {
        // You can access or modify parameters by name from the "parameters" dictionary,
        // e.g. parameters["paramName"].

        // If you're in a property setter, you can use parameters["value"] to get the setter value.

        // For generic methods, use parameters["T1"], parameters["T2"], etc. to get the
        // type-parameter values.

        // Return True to continue execution, return False to stop execution and return
        // immediately. For methods that return values, in this case the default value for
        // the return type is returned.
    }   
}
Exit Aspect: Executed just before control leaves the method or property. To create an exit aspect, create an attribute class (i.e. one that inherits from Attribute) that implements IExitAspect.

public class MyExitAspectAttribute : Attribute, IExitAspect
{
    public void Execute(MemberInfo memberInfo,
        IDictionary<string, object> parameters, 
        object returnValue, 
        TimeSpan duration)
    {
        // Similar to entry attribute, but you also get the value that is being
        // returned in "returnValue" (NULL for void methods).

        // You get the time taken to execute the method/property in "duration".
        // Comes in handy for instrumentation.
    }
}
Error Aspect: Executed if an exception is thrown within the method or property. To create an error aspect, create an attribute class (i.e. one that inherits from Attribute) that implements IErrorAspect.

public class MyErrorAspectAttribute : Attribute, IErrorAspect
{
    public bool Execute(MemberInfo memberInfo, 
        IDictionary<string, object> parameters, Exception ex)
    {
        // "ex" has the exception that was thrown. This aspect is called from
        // a generated "catch" block.

        // After you're done, return True to rethrow the exception, or False to
        // swallow it. If you swallow it and another error aspect rethrows, the final
        // result will be that the error is rethrown.
    }
}
Terms of Use
  • The NuGet packages are free to download and use.
  • The source code is provided under the GPL v3 License. You can see it here.
  • As with most things free, do not blame me if running this blows up your computer or infects you with Hepatitis.
  • If there are legit issues, please do log a GitHub issue.
Privacy
  • These are libraries, not hosted solutions.
  • Nowhere does Aspects for .NET ask you for or store any personal information.
  • The page you're looking at right now has a rudimentary Google Analytics scriptlet.
  • Identifiable information related to usage and download of the server installation is not tracked as far as I know (unless Google Analytics is doing something I am unaware of).
  • Download of the NuGet package is not tracked except the NuGet download counter itself.