Chapter 9. Aspect composition: example and execution

This chapter covers

  • The ordering of aspects being applied to the same code
  • Using aspect roles in PostSharp to specify order/dependency
  • Composing the order of interception aspects with DynamicProxy
  • Real-world example of composing caching and authorization aspects

As you continue to use AOP, you’ll want to apply multiple aspects to the same pieces of code, and this chapter covers how to compose aspects together in a predictable way. If you’re new to AOP, it’s best to start small until you get the hang of it. Introduce a simple aspect (like logging) to your project and see how it goes. For me, it went well, so I wanted to start creating more aspects to handle more cross-cutting concerns. At some point, though, I’ll run into overlap in the code that aspects get applied to. In chapter 2, we wrote aspects for multiple cross-cutting concerns, all on the same piece of code, repeated here in this listing.

Listing 9.1. Multiple aspects on the same method

I briefly mentioned that ordering was a concern, because the order that C# attributes are applied to code is non deterministic (that is, the order you put attributes on code isn’t necessarily maintained by the C# compiler).

The goal for this chapter is to create two projects that demonstrate the composition of multiple, complex aspects that keep each individual component loosely coupled (and testable). First, we’ll look at how Castle DynamicProxy works with an IoC tool like StructureMap to apply aspects in the appropriate order. Then we’ll look at how PostSharp handles aspect composition by configuring aspect roles and aspect dependencies when using attributes. Finally, we’ll put together a project with real-world aspects to demonstrate and compare the two approaches. This project will use either PostSharp or DynamicProxy, but we’ll build it in such a way as to maximize reuse between the two.

9.1. Using multiple aspects

In some cases, it may not be critical which order the aspects get applied. Consider a method that has both a logging aspect and a background threading aspect applied to it. Table 9.1 shows that logging and threading aspects can be executed in either order without problem. In the second case, logging is performed on a separate thread, but it is still performed successfully.

Table 9.1. Ordering of aspects

Executed first

Executed second

Result

Logging aspect Background threading aspect First, the log is written to. Then, the threading aspect takes over and runs the method on a new thread.
Background threading aspect Logging aspect The method is first run on a new thread. Writing to the log takes place on the background thread, but still writes to log successfully.

In both cases, the code is still functional no matter what order is applied (assuming the logger can run on any thread). But the order often does matter. A classic example of this is two common cross-cutting concerns used together on a single method: security and caching. A caching aspect will return whatever value has already been cached, instead of executing the method. A security aspect will determine if the method’s results are allowed to be viewed by the current user (or perhaps perform security trimming on the results, filtering out what the current user is allowed to see). With security and caching together, it’s possible for access to be allowed or denied, and it’s possible for a cached value to be found (hit) or not found (miss). Let’s examine the possible scenarios shown in figure 9.1, which assumes that caching is executed first.

Figure 9.1. Scenario: caching first

You can trace a valid path through figure 9.1, where a (cached) result gets returned without authorization. This is obviously not desirable; authorization should always be performed first, as figure 9.2 illustrates.

Figure 9.2. Scenario: authorization first

In the second scenario (figure 9.2), there’s no way for a value to be returned without authorization being performed. If a method needs to be secure and cached, then we need to be sure that those aspects run in the correct order. Let’s start by looking at the basics of how to compose aspects with PostSharp (you’ve already seen a little bit of this with AspectPriority in the previous chapter). After that, we’ll look at how to do it with Castle DynamicProxy, and then we’ll be ready to look at the real-world example.

9.2. Aspect roles with PostSharp

In the previous chapter, one of the configuration options for PostSharp aspects was AspectPriority. AspectPriority is just a simple number that specifies the order in which aspect attributes are applied. An attribute with a priority of 1 is applied before an aspect of priority 2, and so on. In this piece of code, AnotherAspect will be executed first, followed by LogAspect.

[LogAspect(AspectPriority = 2)]
[AnotherAspect(AspectPriority = 1)]

This is a feature that works with all licenses of PostSharp, including the free Express edition. In many cases, a simple aspect priority may be adequate. Using priority numbers may be tricky to refactor at times. If you’re old enough to remember writing BASIC with line numbers, then you may have faced similar problems, and thus the reason that AspectPriority numbers, like BASIC line numbers, should be spread out (10,20,30 instead of 1,2,3).

If you have more complex dependencies, then a simple ordinal priority may not be enough. A better solution would be to define aspect roles and dependencies, instead of using plain numbers. This is a feature that is limited to the paid commercial license of PostSharp, but it is a feature that allows you to compose aspects in a more robust and clear way, so that the aspects work together how you expect them to.

In this section we’re going to use PostSharp aspect roles to compose aspects, and make them execute in the order we want them to.

9.2.1. PostSharp aspect roles

When you create an aspect, you can specify what role that aspect provides by using the ProvideAspectRole attribute. If I create a caching aspect, then I could specify that it is an aspect that provides the caching role.

Listing 9.2. Specifying an aspect role

The StandardRoles class provides a variety of common cross-cutting concern roles that you can use to configure ProvideAspectRole. Examples are Caching, Security, Threading, Validation, Tracing, and many more.

When I place a ProvideAspectRole attribute on my aspect, I’m giving PostSharp some information about what job this aspect does—what category it falls in. When PostSharp’s post compiler runs, it will know that CachingAttribute is a caching aspect. I can even define multiple aspects that all have a role of StandardRoles.Caching.

Note that StandardRoles is not an enumeration. Each of those members (Caching, Security, and so on), are simply strings. Therefore, you can create your own custom aspect roles by simply using strings.

Once your aspects have aspect roles, you can start to define in what order aspects get applied.

9.2.2. Role dependencies

Once you have defined roles for each aspect, you can define dependencies. However, the dependencies are not between aspects, but between roles. For example, we could say “apply security aspect(s) before applying caching aspect(s).” We wouldn’t say “apply AuthorizationAttribute before CachingAttribute.”

In PostSharp, this is done via another attribute you can put on your aspects called AspectRoleDependency. Here’s an example of making sure that a security aspect always executes before caching aspects.

Listing 9.3. Specifying aspect role dependency

When PostSharp starts modifying code, it will now understand that the authorization aspect should be executed before any caching aspect code.

There are additional options for specifying the dependencies, and the PostSharp documentation contains more details on all the available options. One example is AspectDependencyAction. Instead of Order, you could specify Require: that this aspect requires another aspect of a certain role also be used. Let’s suppose that I define an authorization aspect that requires an aspect in the StandardRoles.Tracing role.

Listing 9.4. An aspect that requires another role

The authorization aspect is requiring that an aspect with the Tracing role is also being used on the same code. However, in my example, it’s not: the authorization attribute is alone. Because of this, when I attempt to build the project, I’ll receive a compile-time error message (figure 9.3), similar to the CompileTimeValidate functionality we saw in chapter 8.

Figure 9.3. Failed aspect dependency

This can be useful if you have two separate aspects that are required to work together on the same piece of code. Next, let’s look at how aspects can be composed with Castle DynamicProxy.

9.3. Composing aspects with DynamicProxy

The work of actually applying DynamicProxy aspects is usually handled by an IoC container, as we’ve seen in previous chapters when I’ve used StructureMap (with a ProxyHelper class), and so that’s where the work of composing aspects is done.

Castle DynamicProxy is an interception tool that was designed for use with Castle Windsor (an IoC container). However, it exists independently of Windsor and is an excellent AOP framework with a rich set of features on its own. StructureMap is another popular IoC tool for .NET that has its own interception framework.

I prefer Castle DynamicProxy’s aspects, but prefer StructureMap to Castle Windsor. I want to get the best of both worlds, so that’s why I’ve used StructureMap and Castle DynamicProxy together in this book (see appendix A for details about other options that are available to you). This is why I wrote the ProxyHelper class in chapter 6 (repeated in the following listing), so that I can more easily use DynamicProxy with StructureMap (or any other IoC container that has functionality similar to StructureMap’s EnrichWithAll).

Listing 9.5. ProxyHelper from chapter 6
public class ProxyHelper {
    readonly ProxyGenerator _proxyGenerator;

    public ProxyHelper() {
        _proxyGenerator = new ProxyGenerator();
    }

    public object Proxify<T, K>(object obj) where K: IInterceptor {
        var interceptor = (IInterceptor) ObjectFactory.GetInstance<K>();
        var result = _proxyGenerator
            .CreateInterfaceProxyWithTargetInterface(
            typeof (T), obj, interceptor);
        return result;
    }
}

Your preference and familiarity will help you determine which tools to use. For this section, I will continue to use StructureMap as my IoC container, and Castle DynamicProxy as my interception tool.

To accomplish ordering with Castle DynamicProxy and StructureMap, just remember that the classes being generated are being used as decorators: the decorator that is closest to the real object is going to be executed last.

9.3.1. Ordering aspects

I’ll start by writing some demonstration aspects and a class that they are to intercept. I plan to have Aspect1 executed first in order, followed by Aspect2, and on to the main implementation in MyClass.

Listing 9.6. A demo class and two demo aspects

To use a single Castle DynamicProxy aspect with StructureMap, I used StructureMap’s EnrichAllWith API, as well as ProxyHelper (in listing 9.7).

To use multiple aspects, you can use a series of calls to the ProxyHelper object inside of a single EnrichAllWith. The Proxify method returns an object that’s been decorated with a dynamically generated class. You can pass this object to Proxify again, to wrap it in another decorator, and so on, as many times as you want.

Because you’re continually wrapping, use the aspect you want applied first in the outermost Proxify call. The innermost aspect will be called last, followed by the original object, of course.

Listing 9.7. Use of multiple aspects on the same code in StructureMap

We start by telling StructureMap to scan the assembly and initialize with the default conventions. That is, if we ask StructureMap for an implementation of IMyClass, it will give us an instance of MyClass. I’m also telling StructureMap to enrich (decorate) the implementation of IMyClass with a dynamic proxy built with the Aspect2 aspect. That aspect, in turn, is being decorated by a dynamic proxy built with the Aspect1 aspect. The end result is a decorator that wraps a decorator that wraps the real object, as shown in figure 9.4.

Figure 9.4. A diagram of what gets returned by StructureMap

Each of these objects has the same interface (IMyClass). The main code calls the outermost decorator’s MyMethod, which turns around and executes the innermost decorator’s MyMethod, which finally calls the underlying object’s MyMethod.

To change the order of the aspects, just change the order in the decoration chain. I could swap Aspect1 and Aspect2, for instance, as in figure 9.5.

Figure 9.5. Switching the order of Aspect1 and Aspect2

To add another aspect, just add another in that decoration chain. If I wanted to add Aspect3, and have it be applied first, I would just add another line inside the EnrichAllWith (and another closing parenthesis).

Keep in mind that this decoration is being applied to only one service (IMyClass). In the last chapter, I talked about how to use PostSharp to multicast attributes. When using a tool like Castle DynamicProxy, you again have to rely on the IoC container to provide for reuse (to use the same aspect on multiple services). I like StructureMap for this because of its flexibility with using conventions. These conventions allow us to multicast the DynamicProxy aspects, so we don’t have to specify every individual class to which the aspects should be applied.

9.3.2. Reducing repetition with custom conventions

PostSharp’s attribute multicasting gave us the ability to specify where to put aspects based on the class name, the namespace, and individual member names.

I could just tell my IoC tool every place I want to use an aspect by using For/Use over and over.

This would be tedious work, but fortunately we don’t have to do it that way. One solution is to define your own convention. StructureMap has a very nice API, allowing you to write any convention you wish. Use of these conventions isn’t limited just to AOP, but they happen to be very useful for applying aspects to groups of services. We could, for example, write conventions that examine the class name or namespace.

Class name convention

In StructureMap, create a class that implements the IRegistrationConvention interface. I’ll create one called RepositoryAspectConvention. This convention will tell StructureMap that any service ending with Repository in its name will also be enriched with two DynamicProxy aspects: Aspect1 and Aspect2.

Listing 9.8. A StructureMap name-based convention for applying aspects

You may have noticed that the Proxify signature is different. This is because Type is passed in to Process as a regular parameter and not a generic type parameter, so I had to write an overloaded Proxify method in the ProxyHelper class, but as you can see in this listing it’s doing the same thing as before.

Listing 9.9. Added overloaded Proxify to ProxyHelper

To use the convention, specify it in the assembly scanner portion of Object-Factory.Initialize, as in shown here.

Listing 9.10. Adding a convention to the assembly scanner

From this point on, any service with a name that ends with Repository will have Aspect1 and Aspect2 applied to it. Since the class name is a string, you can also use regular expressions just as in PostSharp multicasting, or any other convention you wish to define.

Namespace Convention

For instance, I might want to apply the aspects to a namespace, instead of going by class name.

I could simply change the Process method inside of RepositoryAspectConvention to look at the namespace, as in this listing.

Listing 9.11. Using a namespace-based convention

Now, any classes in the ConventionDynamicProxyExample.Repositories namespace will have Aspect1 and Aspect2 applied to it.

And remember, to change the order the aspects are applied, just change the order of the EnrichWith statements.

Now we know how to order aspects with Castle DynamicProxy and with PostSharp. We’re prepared to move on to a real-world authorization and caching example.

9.4. Real-world example: caching and authorization

For the last real-world example of this book, we’re going to focus on two aspects: a caching aspect and an authorization aspect, and we’ll be using and applying material from the entire book.

The aspect composition of an authorization aspect and a caching aspect is an example where the ordering is very important. Recall the diagrams earlier in the chapter where I showed that authorization must be executed before caching to prevent an unauthorized user from seeing data that they shouldn’t.

In this section, I’ll create a program that calls a single service and outputs the results to Console. The service will return the budget for a given account number.

Only users in a manager role are allowed to perform this service operation. Since there could be many services and service operations, we’ll create a security aspect to lock down access.

The budget figures could come from multiple systems, including 3rd party legacy systems, and getting results from those systems can be quite slow. Caching the operation reduces the load on the legacy machine, improves the speed of the overall user experience, and since budgets don’t change very often, caching the results is perfectly acceptable. (Our implementation will be hardcoded and fast, but it’s always important to analyze the problem to see if caching is appropriate or not). To handle the caching, we’ll use a caching aspect very similar to the one we wrote in chapter 4.

We’ll start by building out all of the project that we can without using a specific AOP tool. Then, I’ll create the PostSharp implementation, followed by the Castle DynamicProxy implementation. Everything will be reusable between the two examples, save for the aspects themselves.

9.4.1. Application architecture

The application will consist of four major areas of functionality: the configuration of dependencies (with IoC), the services, the UI of the program, and the aspects. Figure 9.6 shows the ordering of authorization and caching. Figure 9.7 shows a high-level view of the architecture.

Figure 9.6. The correct ordering of authorization and caching.

Figure 9.7. A high-level view of the architecture

Start by creating a new Console project in Visual Studio.

I want to make the architecture as loosely coupled as possible, no matter which AOP tool is being used. Every class will be built to an interface when appropriate to help make testing in isolation easier. The cross-cutting concern objects as well as the services will be configured and supplied by an IoC tool. I’ll again use StructureMap, so let’s start by configuring the dependencies.

Dependency configuration

Start by adding StructureMap to the project.

I will stick to the default convention (for example, MyService corresponds to IMyService). Since I’m using a Console app, I’ll put the StructureMap initialization code right at the start of the Program’s Main method as shown here.

Listing 9.12. Configure StructureMap with default conventions

Note that we’re not configuring any aspects here until we switch to Castle DynamicProxy.

Services

There will be three services. These services are very simplified demonstration services, that would in reality contain much more complex code and more methods, that would interact with your database or other specific technologies, depending on the requirements of your software.

Let’s start with the budget service (in the following listing), since that is the main functionality of this project. It will have one method to return the budget amount, and I’ll make sure to program to an interface.

Listing 9.13. Budget service, interface, and implementation

Since we’ll be using caching, let’s next define a caching service (listing 9.14). In a real application, this would likely be a wrapper of cache functionality such as ASP.NET’s Cache, Windows Azure cache, or some other caching technology. Since this example is demonstrating AOP and not a specific caching technology, I’ll make it a static memory cache just as we did in chapter 4. It will only be a wrapper for a static Dictionary object, and once again I’ll program to an interface.

Listing 9.14. A static caching service

Finally, since we need role-based authorization, I’ll create a UserRepository service. A real service would have a lot more functionality, but in our example you only need to get the current user’s roles. A real implementation would likely query a database and/or interact with an authentication framework like ASP.NET Forms Authentication (which we explored back in chapter 1). For this example in the next listing, I’ll hardcode it to return "Manager". Later on, you can change this to something else to see what happens when the user is not authorized.

Listing 9.15. User repository

I put all of these services into a Services folder in the project (and the AuthAndCaching.Services namespace).

Console UI

The Console UI for this example will be very simple. It will attempt to retrieve the budget for an arbitrary account and output it to the Console. We’ll do it twice, since caching is a part of this example.

Listing 9.16. The main UI of the program

I’ll also add in a try/catch just in case authorization fails, as shown here.

Listing 9.17. The main UI prepared for exception

Now, if authorization is working and an unauthorized user tries to run this program, then they will only see an exception message (an Access Denied error, probably).

Concerns

Recall in chapter 6 when I showed you how to write thin aspects (like in figure 9.7). The aspect delegates as much logic as it can to another object (let’s call it a concern object, because it handles the cross-cutting concern).

I’m going to do that for this real-world example for two reasons:

1.  It makes it possible for us to write a project that is loosely coupled to the AOP framework. Not only does this give us the benefit of easier long-term maintainence, but it also allows us to more easily write unit tests.

2.  Second, it makes it possible for me to switch from using PostSharp (in section 9.4.2) to using Castle DynamicProxy (in section 9.4.3) with minimal changes.

The first concern we’ll write is for caching. This concern will be built to an interface, and it will use the caching service from listing 9.14. I want two methods on this concern’s interface: one for OnEntry and OnSuccess.

Note

I’ve chosen to use the same naming convention that PostSharp uses, but you can name these whatever you want. See listing 9.18.

Listing 9.18. Caching concern interface and constructor

The OnEntry method in listing 9.19 will be called before the method is executed. It will also be responsible for building the cache key, checking the cache, setting the return value (if necessary), and determining if the method should be aborted (not executed) or not. I’ll use the simple "ToString" cache key building strategy here, which you could replace with other strategies as discussed in chapter 4.

Listing 9.19. OnEntry of the caching concern

Note that at this point, we still have not added any AOP libraries to the project. We have yet to take any dependencies.

You may have also noticed the IMethodContextAdapter interface, the details of which are in the following listing. This interface is responsible for giving us information about the method, its arguments, and so on.

Listing 9.20. Adapter interface to get method context
public interface IMethodContextAdapter {
    object Tag { get; set; }
    object ReturnValue { get; set; }
    string MethodName { get; }
    object[] Arguments { get; }
    void AbortMethod();
}

You’ll see in the next sections that we’ll use the adapter pattern to allow our concern objects to stay independent of a specific framework, as well as keep the concern objects easily testable. These adapters will wrap the APIs that we’ve seen in previous chapters (for example, MethodExecutionArgs and IInvocation) and make them conform to the IMethodContextAdapter interface.

The OnSuccess method (shown in the following listing) will be called after the method has been executed. This will only occur if the method has not been aborted, so this is where the return value gets cached.

Listing 9.21. OnSuccess of the caching concern

The authorization concern (the second concern) will be constructed similarly. It will have a dependency on IUserRepository, and it will need an OnEntry method in the interface as shown here.

Listing 9.22. Authorization concern interface and constructor

The OnEntry code will run before the method is executed. It will use the service to determine if the current user is allowed to access the method. If they are, then the concern will return without doing anything else. If they aren’t, then the concern will keep the method from executing, and perform whatever action should occur when unauthorized access is attempted. In my example in the next listing, I’m going to throw an exception with an “Access denied” exception, but you could instead return a 0, null, empty string, and so on, if appropriate.

Listing 9.23. OnEntry of the authorization concern

Though the pattern is similar, note that the AuthorizationConcern has the role parameter to deal with. This role must be passed in from the aspect, in addition to the method context object.

Now we have everything we need in place except for the aspects themselves. Let’s start with PostSharp.

9.4.2. PostSharp

I’ll create a caching aspect called CachedAttribute. It will inherit from PostSharp’s OnMethodBoundaryAspect base class.

In keeping with the thin aspect strategy, this aspect (next listing) will mostly be delegating its work to an ICachingConcern object. I’ll make that object a private member of the aspect, and I’ll populate it in the RuntimeInitialize method.

Listing 9.24. PostSharp caching aspect

In listing 9.25 I’m using an implementation of IMethodContextAdapter for PostSharp called PsharpMethodContextAdapter. This is a class that wraps the MethodExecutionArgs functionality that we need (note that it does not wrap everything, so you could consider this class a façade as well as an adapter).

Listing 9.25. PostSharp method context adapter

Other than create an adapter object, the only other thing that this aspect does is delegate to the concern’s OnEntry and OnSuccess methods. This is what a thin aspect should look like: just the bare minimum.

The aspect is now complete, so put an attribute on GetBudgetForAccount to tell PostSharp that this method should be cached.

[Cached]
public decimal GetBudgetForAccount(string accountNumber) {
    var rand = new Random();
    return rand.Next(1000, 5000);
}

If you run the program now, you should see that the cache aspect is being executed. There should be a cache miss, followed by a cache hit, just like figure 9.8.

Figure 9.8. The thin logging aspect, repeated from chapter 6

Figure 9.9. Execution, using budget service with caching aspect

Right now, there is no checking to see if the user is in a role that’s authorized to view the budget data. So the next step is to write an authorization aspect. I’ll create an AuthorizedAttribute class, also inheriting from OnMethodBoundaryAspect. This aspect will have an explicit constructor that allows us to specify which role is allowed to access a method. That role will be stored as a private member. This aspect (in the following listing) will also be a thin aspect, so it will lean on an IAuthorizationConcern object (initialized at runtime) for most of its functionality.

Listing 9.26. PostSharp authorization aspect

As before, OnEntry is creating an adapter instance, and delegating everything else to the concern.

Put an attribute on GetBudgetForAccount and specify the "Manager" role with an attribute constructor.

[Cached]
[Authorized("Manager")]
public decimal GetBudgetForAccount(string accountNumber) {
    var rand = new Random();
    return rand.Next(1000, 5000);
}

Execute the program. Since we didn’t specify any aspect roles or aspect priority, one possible outcome could be figure 9.10.

Note

This is a possible outcome: the order of C# attributes is not guaranteed by the compiler one way or the other.

Figure 9.10. Execution again, with caching and authorization aspects, undefined composition

There’s a problem here. On the second attempt, there was no authorization being performed. Caching is running first, and results are being returned without even checking authorization. To compose these aspects properly, we have to define an aspect role for CachedAttribute and define an AspectRoleDependency for AuthorizedAttribute, as in this listing.

Listing 9.27. Aspect composition of caching and authorization

And now when you execute the program, authorization is performed both times, as seen in figure 9.11. In this particular case, you could also use AspectPriority setting and achieve the same result.

[Cached(AspectPriority = 20)]
[Authorized("Manager", AspectPriority = 10)]
public decimal GetBudgetForAccount(string accountNumber) {
    var rand = new Random();
    return rand.Next(1000, 5000);
}

So now we have the complete architecture defined. At this point, you could add more methods to the budget service interface, and more services, while keeping the cross cutting concern logic in separate, testable classes.

And because of this architecture, we easily switch over to Castle DynamicProxy.

Figure 9.11. Execution, with authorization and caching composed correctly

9.4.3. Castle DynamicProxy

To do the same thing with Castle DynamicProxy, we only need to write two more thin aspects and one adapter class. Instead of using attributes, we’ll use the IoC configuration to specify where the dynamic proxies will be used.

Write two new aspects

The caching aspect will look similar to the PostSharp aspect. I’ll call it CachedInterceptor, and it will implement DynamicProxy’s IInterceptor interface. I can use plain constructor injection on this class to get an ICachingConcern instance.

Because this is an interception aspect and not a boundary aspect, the Intercept method in the next listing contains code that’s a combination of the code in OnEntry and OnSuccess in the previous section.

Listing 9.28. DynamicProxy caching aspect Intercept code

I have to create that new adapter class, but this time it will wrap an IInvocation object.

Listing 9.29. Castle DynamicProxy method context adapter

Similarly, I will create an AuthorizedInterceptor class, as shown next. This will also use constructor injection to get an IAuthorizationConcern instance, but it will also have a string parameter to specify the role (for example, "Manager").

Listing 9.30. Constructor and members of the Authorization interceptor

The Intercept body (listing 9.31) will be similar to the cache interceptor. Notice that the role is being passed as a second parameter to OnEntry. I did not make role a member of the method context adapter, since it’s not method context, but it is information that the concern needs.

Listing 9.31. Authorization Intercept code

Remove PostSharp attributes

If you’re following along and using the same project files, the next thing you should do is make sure to remove the Cached and Authorized attributes from GetBudgetForAccount.

public decimal GetBudgetForAccount(string accountNumber) {
    var rand = new Random();
    return rand.Next(1000, 5000);
}

Otherwise, you’ll end up with authorization and caching being executed twice.

Change IoC configuration

With PostSharp, the IoC container was only used to initialize the services. With Castle DynamicProxy, we have to lean on it to apply the aspects as well.

Start with the same ObjectFactory.Initialize code as before. Now we need to tell StructureMap two things: (a) how to configure the authorized aspect for the "Manager" role, and (b) which aspects to use on BudgetService.

Once again, I’ll be making use of the ProxyHelper to make StructureMap and Castle DynamicProxy get along. I’m going to add a new Proxify overload (in listing 9.32) that can work with named instances. In StructureMap, you can give mappings an arbitrary name, and if we want to use the authorization aspect for multiple roles (for example, Manager, Administrator, Editor, and so on), we’ll need to do this. The only difference is that it takes an additional string parameter and uses GetNamedInstance instead of GetInstance.

Listing 9.32. Another Proxify overload in ProxyHelper

Once that’s in place, we need to configure a named instance of AuthorizedInterceptor that uses "Manager" as the role. Since the role is passed in to the constructor, we’ll need to use StructureMap’s ability to inline constructor dependencies, as shown in the next listing.

Listing 9.33. Name a mapping for Manager authorization

From this point on, if I specify that the "ManagerAuth" named instance is used, then AuthorizedInterceptor’s role parameter will be "Manager". I could create other named instances for other roles as well.

Finally, I’ll just enrich the IBudgetService as we’ve done in previous chapters, this time using the new Proxify overload to handle the authorization aspect.

Take careful note of the order of the enrich statements in the following listing: caching needs to be the innermost aspect. This way, it is applied first, and we ensure that caching will be executed after authorization.

Listing 9.34. Complete IoC initialization

Run the program, and you’ll see the Console output (figure 9.12) is the same that we saw with PostSharp (figure 9.11).

Both projects are able to handle multiple complex aspects and complex dependencies while remaining testable and loosely coupled (the Castle DynamicProxy version is easier to test, of course, as we covered in chapter 6).

Figure 9.12. Execution with Auth and Cache aspects, using Castle DynamicProxy

9.5. Summary

Ordering is often important, and both runtime and compile-time AOP tools give you the ability to specify ordering. Runtime AOP will often rely on the IoC container to specify ordering (as well as for reuse). With PostSharp, using attributes alone in C# is not deterministic, so you have to specify the ordering through the PostSharp API. You can use a simple AspectPriority or the more robust aspect roles configuration options.

I’ve taken you through my personal journey, using the tools that I know the best and that I feel comfortable with. I like Castle DynamicProxy when writing basic aspects, provided I have the ability to intercept all the necessary objects via my IoC tool of choice (which is usually StructureMap). When the situation calls for more power, features, and flexibility, I like to use PostSharp as a compile-time AOP tool. Using a combination of the two in the same project is absolutely feasible, given their strengths and weaknesses.

I’ve explored AOP techniques and tools in this book, but there’s so much more to explore in your personal journey. Use the tools and techniques that you like, that you feel comfortable with, and that enable you to write high-quality code.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset