Monday, February 6, 2017

Aspect-Oriented Programming: Implementing caching with AOP


Introduction to AOP



Aspect-oriented programming (AOP) is, according to Wikipedia, “a programming paradigm that aims to increase modularity by allowing the separation of crosscutting concerns.” It deals with functionality that occurs in multiple parts of the system and separates it from the core of the application, thus improving separation of concerns while avoiding duplication of code and coupling.
The biggest advantage of AOP is that you only have to worry about the aspect in one place, programming it once and applying it in all the places where needed. 

There are many uses for AOP, such as:
  • Implementing logging in your application.
  • Using authentication before an operation (such as allowing some operations only for authenticated users).
  • Adding caching to certain method calls.
  • Global error handling.
  • Changing the behavior of some methods.

In the .NET Framework, the most commonly techniques to implement AOP are post-processing and code interception. The former is the technique used by PostSharp (postsharp.net) and the latter is used by dependency injection (DI) containers such as Castle DynamicProxy and Unity’s Interception feature. These tools usually use a design pattern named Decorator or Proxy to perform the code interception.
In this two-part blog post I’ll take a look on the two approaches that don’t need a DI container:

  1. .NET’s RealProxy class uses reflection in order to build a dynamic proxy for code interception.
  2. PostSharp adds intermediate language (IL) code in a post-compilation step. 

I’ll try to finish with my personal conclusions.

Part 1: Implementing a caching decorator with RealProxy class

The RealProxy class gives you basic functionality for proxies. It’s an abstract class that must be inherited by overriding its Invoke method and adding new functionality. This class is in the namespace System.Runtime.Remoting.Proxies.
Below we define a caching decorator using RealProxy.
public class CachingProxy<T>: RealProxy
{
    private readonly T _decorated;

    public CachingProxy(T decorated): base(typeof (T))
    {
        _decorated = decorated;
    }

    public override IMessage Invoke(IMessage msg)
    {
       var methodCall = msg as IMethodCallMessage;

       var methodInfo = methodCall.MethodBase as MethodInfo;

       MethodResultCache cache = MethodResultCache.GetCache(methodInfo);

       object result = cache.GetCachedResult(methodCall.InArgs);

       if (result == null)
       {
           try
            {
                result = methodInfo.Invoke(_decorated, methodCall.InArgs);

                cache.CacheCallResult(result, methodCall.InArgs);
            }
            catch (Exception e)
            {
                return new ReturnMessage(e, methodCall);
            }
        }

        return new ReturnMessage(result, null, 0, methodCall.LogicalCallContext, methodCall);
    }
}

In the constructor we pass the type of the decorated class to the base
class. Next we override the Invoke method that receives an IMessage parameter. It contains a dictionary with all the parameters passed to the original method call. After extracting the MethodInfo we can add the aspect we need before calling the method. 
We use a caching helper (MethodResultCache) to check for cached results and only if we don’t find them we call the decorated method and cache the results.

The caching helper that is used has methods for getting cached results or adding results to cache. The implementation is not important for now, will be available for download.


public interface IMethodResultCache
{
    object GetCachedResult(IEnumerable<object> arguments);

    void CacheCallResult(object result, IEnumerable<object> arguments);
}


We will use this caching decorator on a simple repository class with the following contract:

public interface IUsersRepository
{
    List<User> GetAll();

    User GetById(int id);
}

The actual implementation that gets the data from the DB is also out of the scope of this article. Imagine a EntityFramework implementation or other basic data access alternative.

To use the decorated repository, we must use the GetTransparentProxy method, which will return an instance of IUsersRepository. Every method of this instance that’s called will go through the proxy’s Invoke method. To ease this process, we create a Factory class to create the proxy and return the instance for the repository:


public class UsersRepositoryFactory
{
    public static IUsersRepository CreateRealProxy()
    {
        var repository = new UsersRepository();

        var dynamicProxy = new CachingProxy<IUsersRepository>(repository);

        return dynamicProxy.GetTransparentProxy() as IUsersRepository;
    }
}
First we initialize the concrete implementation of the repository interface, then we decorate it with our caching proxy defined above.

This is how the calling code would look like:

[TestMethod]
public void TestRealProxy()
{
    IUsersRepository repository = UsersRepositoryFactory.CreateRealProxy();

    repository.GetById(1);
    repository.GetById(1);
    repository.GetById(2)
}

And these are the test results for a run, the messages are writen to output by the caching helper's GetCachedResult method:

As you can see we created a dynamic proxy that allows adding aspects to the code, with no need to repeat it. If we wanted to add a new aspect, you’d only need to create a new class, inherit from RealProxy and use it to decorate the first proxy.

Instead of using the factory we can imagine a different scenario of using annotations to mark cacheable classes/members and linking them to the caching proxy. This approach is used by Unity's interception feature.


Part 2: to be continued


Source code link.

Happy coding.

No comments:

Post a Comment