AOP: Be aware where your point cuts are

time to read 3 min | 520 words

So, this issue cause some head scratching today. We are using WIndsor's Automatic Transaction Management with NHibernate's flush-on-commit option, so if a transaction doesn't commit, nothing is written to the database.

Anyway, this is a story about refactoring, and what it showed us. We performed the following refactoring:

image

Some things that is important to understand, the LoginController is decorated with [Transactional], and there is a [Transaction] attribute on CreateUserLoggedInAuditRecord.

When it was on the controller, it just worked. When we moved it to its own class, it didn't work. To be rather more exact, it worked, it just never committed the transaction. That was weird. After some head scratching I found out that I forgot to put [Transactional] on the UsageRegistrationImpl. With a small smile of geeky  triumph, I run the code again. It didn't save.

That was really worrying, and I had no idea what was going on. Since this is rarely popular, I repeatedly run the code, hoping that something would turn up and that no one would pull the old quote about insanity.

After a few repetitions, I suddenly saw the light.

image

It had to do where I placed the pointcut. A pointcut, in AOP terms, is where the AOP can interfere with the running code. Let us take a look at how it worked when we used the LoginController directly. Because we (well, the transaction facility) asked the container to create an interceptor for it, we got the following classes at runtime:

image

The login controller is the original class, the login controller proxy was generated at runtime, and any invocation of any of its methods would fire the transaction interceptor, so it would get a chance to create/rollback/commit a transaction if needed. Since those methods are virtual, this means that even if I am calling methods on the same class, they will be intercepted correctly.

Now, when I moved to the interface + implementing class, we have a different behavior. Now, we use the interface pointcuts in order to inject behavior, it looks like this:

image

Windsor will create a proxy interface implementation that would call the AOP interceptors and will forward to the UsageRegistrationImpl.

The problem was with the RegisterUserLoggedIn method. It was similar to this:

public virtual void RegisterUserLoggedIn(string username)
{
	// do other things
	CreateUserLoggedInAuditRecord(username);
}

[Transaction]
public virtual void CreateUserLoggedInAuditRecord(string username)
{
	//do database stuff
}

Given the story so far, you can obviously see the problem. When we call the CreateUserLoggedInAuditRecord() method, we call it from the UsageRegistrationImpl class, so we never pass through any of the pointcuts.

When we used the method from the controller directly, we made a virtual method call, which was intercepted, but since in this case, we were using the interface as our pointcut, this simply by passed the whole thing.

That was an interesting lesson, and one that I'll need to remember for the future.