Introducing the CachedRepository Pattern

Introducing the CachedRepository Pattern

In this first part of a series on adding support for caching to the Repository Pattern, I’d like to show how to very simply control whether or not caching is performed on a per-repository basis through the use of an Inversion of Control Container.  In this case, I’ll be using StructureMap with ASP.NET MVC 3.  One of the primary goals of this implementation is to follow principles of object oriented design, in particular the Single Responsibility Principle and the Don’t Repeat Yourself Principle.

To start, take a look at the MVC Music Store sample application.  The Jan 13 2011 release doesn’t include any usage of the Repository pattern, so the first thing we will do is add a simple repository to it.  The initial HomeController, which you can view online here, includes code like this:

 

 

The first step of course is to pull out the data access into an interface, implement the interface, and inject the interface into the class via its constructor.  The revised HomeController looks like this:

 

In order to make this work, we had to create an interface and a default implementation of that interface, like so:

 

Finally, we had to configure StructureMap in ASP.NET MVC 3, which you can also do by issuing the following NuGet command:

With StructureMap wired up, we simply need to ensure that it knows to provide the EfAlbumRepository class whenever a type of IAlbumRepository is requested, which this achieves:

 

Now at this point if you’ve been following along, you should be able to run the Music Store application and it should do exactly what it did before (which, ideally, is to run correctly and show you the store’s home page).  Let’s assume that’s what you get.

Implementing a Cached Repository

At the most basic level, implementing a cached repository is simply a matter of overriding the methods of the base repository implementation (which must be marked as virtual), and then updating the IOC container’s registration to use the new type.  Implementing a CachedAlbumRepository would look something like this:

 

Note that although the above code does use best practices for implementing caching logic, it is using a hard-coded cache duration, which should be avoided.  We also would find that as we implemented this approach again and again for various repository methods, we would have a lot of duplicate code, in violation of the DRY principle.  The next article in this series will address these concerns.

The above code is an example of the Proxy (or perhaps Decorator) design pattern.  Proxies are all about controlling access, and the CachedAlbumRepository controls access to the EfAlbumRepository by first checking to see whether the data exists in the cache (one could make the argument that this is about adding behavior to the underlying repository, in which case the Decorator pattern, which has the same structure, would be the more appropriate label).  PluralSight has a nice video series on Design Patterns.

Wiring it up in the IOC container is simply a matter of replacing the existing x.For<IAlbumRepository().Use<EfAlbumRepository>(); line with this one:

Now, if you hit the site and monitor the debug output using a tool like DebugView, you should see something like this as you start and then refresh the page:

DebugView_CachedRepository

With this in place, you’re now ready to start controlling whether your individual repositories are using caching or not through your IoC container.  This makes performance testing and tuning much easier, as you’re now able to compare test results with and without caching for any given part of the application by changing one line of code in one class (the IoC.cs class in this case).

You can view and download the code used in this article from the MvcMusicStoreRepositoryPattern fork on CodePlex.

In the second article in this series, we look at how to achieve the CachedRepository pattern using the Strategy Pattern, rather than simple inheritance.

  • Twitter Trackbacks for Introducing the CachedRepository Pattern [stevesmithblog.com] on Topsy.com

    Pingback from Twitter Trackbacks for Introducing the CachedRepository Pattern [stevesmithblog.com] on Topsy.com

  • Taswar

    Yeah I would say this is just a repository pattern than cache repository, the data stored is in the cache thats all but as you said the http cache does need refactoring.

    Something like this would help, so that one doesnt have to cast back the item, and just wrap the HttpRuntime.Cache into the Impl class.

    public interface ICache

    {

    /// <summary>

    /// Get the Type from Cache with key

    /// </summary>

    /// <typeparam name="T"></typeparam>

    /// <param name="key"></param>

    /// <returns></returns>

    T Get<T>(string key);

    /// <summary>

    /// Store the object with key into cache

    /// </summary>

    /// <param name="obj"></param>

    /// <param name="key"></param>

    void Store(object obj, string key);

    /// <summary>

    /// Remove the object from cache with key

    /// </summary>

    /// <param name="key"></param>

    void Remove(string key);

    /// <summary>

    /// Store the object into cache with key and absolute expiration

    /// </summary>

    /// <param name="obj"></param>

    /// <param name="key"></param>

    /// <param name="expiry"></param>

    void Store(object obj, string key, DateTime expiry);

    /// <summary>

    /// Store the object into cache with key and sliding expiration

    /// </summary>

    /// <param name="obj"></param>

    /// <param name="key"></param>

    /// <param name="timeSpan"></param>

    void Store(object obj, string key, TimeSpan timeSpan);

    /// <summary>

    /// Clears the cache.

    /// </summary>

    void ClearCache();

  • Building a CachedRepository via Strategy Pattern : Steve Smith’s Blog

    Pingback from Building a CachedRepository via Strategy Pattern : Steve Smith’s Blog

  • Anon

    How about using NHibernate’s 2nd level cache? ;)

  • ssmith

    You’re welcome to, but that’s a different layer of abstraction. The pattern above exists at (or above) the Repository abstraction. nHibernate, and any other O/RM, exists within or below the Repository abstraction. You can certainly apply caching at any number of layers within an application, and the answer to when and where you should do so is, of course, "it depends."

  • Common Design Patterns Resources

    Last night I gave a presentation at the Cleveland .NET SIG on Common Design Patterns.&#160; The turnout was great, so much so that the group ran out of pizza and chairs, so thanks to everyone for taking the time to come out!&#160; Thankfully the A/C held

  • jeremy simmons

    CachedAlbumRepository doesn’t implement either proxy or decorator. It is an example of inheritance.

    This is an example of the decorator pattern.

    interface IAlbumRepository {

    IEnumerable<Album> GetTopSellingAlbums(int count);

    }

    EfAlbumRepository : IAlbumRepository {

    IEnumerable<Album> GetTopSellingAlbums(int count)

    { // fetch from EF Context }

    }

    CachedAlbumRepository : IAlbumRepository {

    IAlbumRepository wrappedRepository;

    CachedAlbumRepository(IAlbumRepository wrapped)

    {

    this.wrappedRespository = wrapped;

    }

    IEnumerable<Album> GetTopSellingAlbums(int count)

    {

    // if cached return cached Albums

    // fetch Albums from EF Context

    // store Albums in cache

    // return Allbums

    }

    }

    }

  • Steve Smith

    Inheritance and Proxy/Decorator are not mutually exclusive. I demonstrate how to use the Strategy pattern to achieve a similar design to what you’re showing in your comment here: stevesmithblog.com/…/building-a-cach

  • Daniel

    Nice. I’m using another approach in my project, where I enable the cache per entity (db.CacheProvider.EnableFor<album>()) and I then get auto caching for id queries etc.

    When making a “complex” query cacheable, the generated SQL and params for the SQL query is turned into a checksum. You activate it by marking the query as cacheable, e.g. store.Query<album>().Cacheable().Where(a => a…..).ToEnumerable();

    //Daniel</album></album>