Adding Aspects to Multiple Declarations Using Attributes

Once have written an aspect we have to apply it to the application code so that it will be used. There are a number of ways to do this so let's take a look at one of them: custom attribute multicasting. Other ways include XML Multicasting (see the section Adding Aspects Using XML) and dynamic aspect providers (see more in the section Adding Aspects Programmatically using IAspectProvider).

This topic contains the following sections:

Applying to all members of a class

When we are trying to apply a method level aspect we can place an attribute to each of the methods.

public class CustomerServices

As our codebase grows this approach becomes tedious. We need to remember to add the attribute to all of the methods on the class. If you have hundreds of classes, you may have thousands of methods you need to manually add the aspect attribute to. It's an unsustainable proposition. Thankfully, there is a way to make this easier. Instead of applying your aspect on each method you can add that attribute to the class and PostSharp will ensure that the aspect is applied to all of the methods on that class.

Applying an aspect to all types in a namespace

Even though we don't have to apply an aspect to all methods in all classes in our application, adding the aspect attribute to every class could still be an overwhelming task. If we want to apply our aspect in a broad stroke we can make use of PostSharp's MulticastAttribute.

The MulticastAttribute is a special attribute that will apply other attributes throughout your codebase. Here's how we would use it.

  1. Open the AssemblyInfo.cs, or create a new file GlobalAspects.cs if you prefer to keep things separate (the name of this file does not matter).

  2. Add an [assembly:] attribute that references the aspect you want applied.

  3. Add the AttributeTargetTypes property to the aspects's constructor and define the namespace that you would like the aspect applied to.

    [assembly: OurLoggingAspect(AttributeTargetTypes="OurCompany.OurApplication.Controllers.*")]

This one line of code is the equivalent of adding the aspect attribute to every class in the desired namespace.

Note Note

When setting the AttributeTargetTypes you can use wildcards (*) to indicate that all sub-namespaces should have the aspect applied to them. It is also possible to indicate the targets of the aspect using regex. Add "regex:" as a prefix to the pattern you wish to use for matching.

Excluding an aspect from some members

Multicasting an attribute can apply the aspect with a very broad brush. It is possible to use AttributeExclude to restrict where the aspect is attached.

[assembly: OurLoggingAspect(AttributeTargetTypes="OurCompany.OurApplication.Controllers.*", AttributePriority = 1)] 
[assembly: OurLoggingAsepct(AttributeTargetMembers="Dispose", AttributeExclude = true, AttributePriority = 2)]

In the example above, the first multicast line indicates that the OurLoggingAspect should be attached to all methods in the Controllers namespace. The second multicast line indicates that the OurLoggingAspect should not be applied to any method named Dispose.

Note Note

Notice the AttributePriority property that is set in both of the multicast lines. Since there is no guarantee that the compiler will apply the attributes in the order you have specified in the code, it is necessary to declare an order to ensure processing is completed as desired.

In this case, the OurLoggingAspect will be applied to all methods in the Controllers namespace first. After that is completed, the second multicast of OurLoggingAspect is performed which then excludes the aspect from methods named Dispose.

See Overriding and Removing Aspect Instances for more details about excluding and overriding aspects.

Filtering by class visibility

Now that you've been able to apply our aspect to all classes in a namespace and its sub-namespaces, you may be faced with the need to restrict that broad stroke. For example, you may want to apply your aspect only to classes defined as being public.

  1. Add the AttributeTargetTypeAttributes property to the MulticastAttribute's constructor.

  2. Set the AttributeTargetTypeAttributes value to Public.

    [assembly: OurLoggingAspect(AttributeTargetTypes="OurCompany.OurApplication.Controllers.*",  
                                AttributeTargetTypeAttributes = MulticastAttributes.Public)]

By combining AttributeTargetTypeAttributes values you are able to create many combinations that are appropriate for your needs.

Note Note

When specifying attributes of target members or types, do not forget to provide all categories of flags, not only the category on which you want to put a restriction.

Filtering by method modifiers

Filtering at a class level may not be granular enough for your needs. Aspects can be attached at the method level and you will want to control filtering on these aspects as well. Let's look at an example of how to apply aspects only to methods marked as virtual.

  1. Add the AttributeTargetTypeAttributes property to the MulticastAttribute's constructor.

  2. Set the AttributeTargetTypeAttributes value to VirtualVirtual.

    [assembly: OurLoggingAspect(AttributeTargetTypes="OurCompany.OurApplication.Controllers.*", AttributeTargetMemberAttributes = MulticastAttributes.Virtual)]

Using this technique you can apply a method level aspect, or stop it from being applied, based on the existence or non-existence of things like the static, abstract, and virtual keywords.

Programmatic filtering

There are situations where you will want to filter in a way that isn't based on class or method declarations. You may want to apply an aspect only if a class inherits from a specific class or implements a certain interface. There needs to be a way for you to accomplish this.

The easiest way is to override the CompileTimeValidate(Object) method of your aspect class, where you can perform your custom filtering. This is the opt-out approach. Have the CompileTimeValidate(Object) method return false without emitting any error, and the candidate target will be ignored. See the section Validating Aspect Usage for details.

The second approach is opt-in. See the section Adding Aspects Programmatically using IAspectProvider for details.

See Also