Quick Introduction to Aspect Oriented Programming (AOP) and Dependency Injection

Years ago I was given a task to optimize some methods in a large business app I had been supporting. Periodically a certain page would take close to a minute to load, while other times it would take mere seconds. We were convinced it was due to certain parameters being passed into one of the service calls on the page, but couldn't necessarily prove it. That is, until we found Aspect Oriented Programming (AOP) and PostSharp.

Aspect Oriented Programming is the "allowing of cross-cutting concerns." Essentially, the ability to cut into the execution of a particular function and intercept it's context. I had heard rumblings about AOP early on in my career, but I had never seen it, or was given a concrete scenario when it would be a good use case. At the time of this long running method, PostSharp was still relatively new, but it made AOP much more accessible than it had been in the past. I decided this was as good of a use case as any.

Aside from being able to intercept the parameter values of the executing method, I had to have some way of measuring the performance impact of my optimizations. I ended up with an aspect that would log the parameter values passed into the method, start a timer on method entry and stop it on method exit, leaving me with the full method execution time. I was able to quickly identify the culprit of the long execution times, optimize the method with a few small code changes and prove that those modifications improved performance by more than 90%.

[Serializable]
public class LoggingAspect : OnMethodBoundaryAspect
{
    public override void OnEntry(MethodExecutionArgs args)
    {
        args.MethodExecutionTag = Stopwatch.StartNew();

        foreach (var argument in args.Arguments)
        {
            Debug.WriteLine("Parameter Value: {0}", argument);
        }
    }

    public override void OnExit(MethodExecutionArgs args)
    {
        Debug.WriteLine(((Stopwatch) args.MethodExecutionTag).Elapsed);
    }
}

Fast forward to this week, I was able to identify another use case for AOP, submit a procurement request for the license, and develop an OnMethodBoundaryAspect. On any Controller that used a particular function, it was necessary to perform additional processing before that function could be used without exception. After a quick audit, I had a dozen Controllers using this function, scattered throughout multiple Areas, and inheriting from different base Controllers. I knew that there had to be a way to intercept the method invocation and perform the processing I needed. The lightbulb finally went off, and I quickly proposed AOP as a solution.

[MulticastAttributeUsage(MulticastTargets.Method)]
[Serializable]
public class DoWorkAttribute : OnMethodBoundaryAspect
{
	public override void OnEntry(MethodExecutionArgs args)
    {
    	// Do something
    }
}

This allowed me to place this aspect on the Controllers that required the additional processing, and the MulticastTarget of Methods, means it will run before any method on that Controller.

[DoWork]
public class HomeController : Controller
{
	// Aspect will execute here...
	public ActionResult Index()
    {
        return View();
    }
    
    // ...and here
    public ActionResult Edit()
    {
    	return View();
    }
}

There's always a gotcha, though.

The work I wanted to perform in this aspect has a dependency on a service class. Normally we would simply create a constructor and pass in our dependecy, but due to the application lifecycle this, as well as property injection, is not possible.

Typical constructor injection:

public class WorkClass
{
	private readonly IServiceClass _serviceClass;
    
    public WorkClass(IServiceClass serviceClass)
    {
    	_serviceClass = serviceClass;
    }
    
    public async Task<object> DoWork()
    {
    	return await _serviceClass.DoSomething();
    }	
}

Luckily, I found this great StackExchange post that lead me to the resolution.

We can utilize a Func to define a method which will retrieve our dependency for us, at the appropriate time in the application lifecycle.

With this in place, our aspect now looks like this:

[MulticastAttributeUsage(MulticastTargets.Method)]
[Serializable]
public class DoWorkAttribute : OnMethodBoundaryAspect
{
	public static Func<IServiceClass> GetServiceClass { get; set; }
    
	public override void OnEntry(MethodExecutionArgs args)
    {
		return GetServiceClass().DoSomething();
    }
}

Then in our dependency injector config, this is resolved as such:

DoWorkAttribute.GetServiceClass = () => container.Resolve<ServiceClass>();

We now have an aspect, utilizing dependency injection, that can be used to do some pre-processing for Controller methods within our app. The example I've used resides inside of an API I've written, that is exposed to multiple applications through NuGet. Assuming I write solid documentation for this API, the consumption of it should be fairly clean with this solution.

Have you used AOP in the past? What are some other great use cases for it?