Data Caching with .Net 4.0 and Asp.Net MVC - part 1

The purpose of this article is to demonstrate a sort-of real-world example of how to use the new .Net 4 Caching Framework, utilising some good extensible programming practises. This isn’t specifically targeted at MVC programmers, but I am using Asp.Net MVC as the host to show the results of fetching data via the cache. If you want, you can download the demonstrate app and walk through it with me.

First of all, an overview of the structure of the application and what we’re going to be doing. The example application and the code snippets below are written in C#.

  • Creating a bog-standard MVC 2 application
  • Use the Entity Framework to read our data from the database, and utilise a repository pattern to wrap up this database interaction
  • Wrap up the new .Net 4.0 caching objects ObjectCache and MemoryCache to give ourselves another extensibility point, and aid unit testing
  • Tie all this together using the MVC front-end

By the way – the tools I am using here are Visual Studio 2010 Professional (Express would work too, although I haven’t tested it), Asp.Net MVC 2, .Net Framework 4 and Sql Server 2008 Express Edition. Although I’ve mentioned it above, there isn’t any unit testing being done in the downloadable demo application (as it’s not the real focus of this article) but, as you will see, the techniques we will implement lend itself very well to being unit testable and you are free to carry out this testing if you wish.

First of all, let’s set up the database and our entities!

I’ve created a simple test database with one table to store some information about Vehicles, and it looks like this:

The schema for the caching demo

Create a new Asp.Net MVC2 project (not the ’empty’ one) – I’ve called mine CachingDemo.Web. We’re going to use the Entity Framework to interact with this database, so go ahead and add a new Entity Data Model item to the project. I’ve chosen to create this data model in the *Models *folder, inside a new folder I’ve created called *Data *which, after we’re done, will give us this sort of structure:

MVC2 project

Right-click on the *Data *folder and add a new ADO.NET Entity Data Model:

Adding a new Entity Data Model to the project

Create your connection to the database. Here I’ve used my own local instance of SqlExpress:

EF2

Keep all the default names and options for saving the connection and entity objects:

EF3

Choose the test database and again just select all the default options for now:

EF4

Just click ‘OK’ through the rest of the options until the wizard is complete and you’re faced with the entity model designer. It should only have the Vehicle entity on it, which exactly maps to our database schema.

Vehicle

That’s all for the Entity Framework stuff for now. Next we need to start creating some objects to help us retrieve this data and actually perform the caching. This next step is where the main focus of the article is, as it actually uses the caching objects and wraps it up into a neat repository class.

We’re also going to wrap up the Cache objects so that we can swap it out later, with either a Mock caching object for unit testing, or to implement a 3rd-party caching framework such as Memcached. Start by creating the interface for our cache provider:

public interface ICacheProvider { object Get(string key); void Set(string key, object data, int cacheTime); bool IsSet(string key); void Invalidate(string key); }

Next, we will provide a default implementation which uses the .Net 4 ObjectCache and MemoryCache objects to provide caching support. In order to do this, you will need to add a reference System.Runtime.Caching assembly:

This default implementation looks like the following:

public class DefaultCacheProvider : ICacheProvider { private ObjectCache Cache { get { return MemoryCache.Default; } } public object Get(string key) { return Cache[key]; } public void Set(string key, object data, int cacheTime) { CacheItemPolicy policy = new CacheItemPolicy(); policy.AbsoluteExpiration = DateTime.Now + TimeSpan.FromMinutes(cacheTime); Cache.Add(new CacheItem(key, data), policy); } public bool IsSet(string key) { return (Cache[key] != null); } public void Invalidate(string key) { Cache.Remove(key); } }

The main points to note here are:

  • We’ve created a private property which allows us to retrieve the actual cache object. This just returns the static .Net4 MemoryCache object. Because it’s static, you will receive the same cache object back every time you call it, and other users using the cache will also be served the same cache object. This is key – once the data is in the cache, it is cached for all users.
  • In our simple implementation of the Set method, we create a cache policy which caches the item for the following 30 minutes. Once the 30 minutes has passed, the item is dropped from the cache.
  • We have another couple of utility methods which allow consumers to check that an item has been cached, and also one which allows us to force the removal of a cached object.

Now that we have an interface for our cache and that the .Net Caching objects are wrapped up, we can go ahead and create our data repository. For our simple demo application we’re just going to use this repository straight from the MVC Controller. For more advanced or complex applications you will probably want to create another business layer on top of this repository which contains more coarse-grained business methods.

Again, we will have an interface for our repository and then implement it, making use of our Entity Framework objects we created earlier. The interface looks like this:

public interface IVehicleRepository { void ClearCache(); IEnumerable GetVehicles(); }

Pretty simply, it defines contract which allows us to get vehicles. It also allows us to clear it’s data cache, which may not be needed in a real application but I’ve included it here for convenience. The implementation of this interface looks like the following:

public class VehicleRepository : IVehicleRepository { protected CachingDemoEntities DataContext { get; private set; } public ICacheProvider Cache { get; set; } public VehicleRepository() : this(new DefaultCacheProvider()) { } public VehicleRepository(ICacheProvider cacheProvider) { this.DataContext = new CachingDemoEntities(); this.Cache = cacheProvider; } public IEnumerable GetVehicles() { // First, check the cache IEnumerable vehicleData = Cache.Get("vehicles") as IEnumerable; // If it's not in the cache, we need to read it from the repository if (vehicleData == null) { // Get the repository data vehicleData = DataContext.Vehicles.OrderBy(v => v.Name).ToList(); if (vehicleData.Any()) { // Put this data into the cache for 30 minutes Cache.Set("vehicles", vehicleData, 30); } } return vehicleData; } public void ClearCache() { Cache.Invalidate("vehicles"); } }

Note that we have a constructor which allows us to specify an implementation of ICacheProvider *(our caching interface), but that also the default constructor just passes it an instance of our *DefaultCacheProvider. Now we can easily create our repository with all the default dependencies but at the same time we have the flexibility to provide another implementation when we need one.

GetVehicles()*is where the main bulk of the work is done. There is a specific pattern at work where which makes working with the cache easy and robust:*

  1. Try and retrieve the vehicle data from the cache using our ‘vehicles’ key.
  2. If the data is not there, we re-create the data from the database (our entity data model). Note that we store the data into the same vehicleData variable from when we tried to read the cache.
  3. If there is any data to store, we set it into the cache using the same ‘vehicles’ key, and set the timeout as 30 minutes.
  4. Return the vehicle data

Our ClearCache() implementation is also there, which simply invalidates our ‘vehicles’ data.

Now we have all we need to go ahead and create the controller and the interface to display and invalidate this data. First we will set up the constructors on our controller – here I have just made use of the Home controller rather than creating a new one. The constructors follow a similar pattern which allow us to specify an implementation of IVehicleRepository, or provide another one for unit testing:

public class HomeController : Controller { public IVehicleRepository Repository { get; set; } public HomeController() : this(new VehicleRepository()) { } public HomeController(IVehicleRepository repository) { this.Repository = repository; }

Continue with the Index action, and retrieve the data from the repository. This is all the data access code we need to write as far as the controller is concerned – because we’ve abstracted all the actual caching code away, we no longer need to worry about whether the data is being cached or not; our controller code will work regardless:

public ActionResult Index() { ViewData["Message"] = "Welcome to my caching demo!"; return View(Repository.GetVehicles()); }

I’ve just left in the ViewData message which was there from the project creation, but changed it slightly. The main point about this code is the call to Repository.GetVehicles(), and the fact that we are passing it as the model to the Index view. I also have another Index action here which acts on form post – as you’ll see in a moment, I have a button on my form which allows us to invalidate the cache from the page for convenience, so we can test that the cache is being dumped and re-created properly. This action looks like the following:

[HttpPost] public ActionResult Index(FormCollection form) { Repository.ClearCache(); return RedirectToAction("Index"); }

On to the view next. The resulting view I have simply writes out the data into a table, with a button underneath allowing us to invalidate the cache. I’ve already put some sample data into my database, which you might also want to do now:

The code for this view (which I have just put into the Index view) looks like this:

Car data:

<% foreach (var vehicle in Model) { %> <% } %>
Id Name Price
<%: vehicle.Id.ToString() %> <%: vehicle.Name %> <%: string.Format("{0:c0}", vehicle.Price) %>
<% using (Html.BeginForm()) { %> <% } %>

Now we have completed the code for the application. Test the application by performing the following steps (make sure you have put in some test data):

  1. Load the page. You should see your test data appear
  2. Alter the data directly on the database using Sql Server Management Studio or some other means which allows you to directly alter the table
  3. Refresh the page. Your changes should not appear, because on this page load the old data is coming from the cache, not the database.
  4. Press the button to invalidate the cache. The page will reload and your database changes will now be shown.

To dive further into the mechanics, when debugging the application, you can place breakpoints around the caching code to see the data going into and being retrieved from the cache. After the first page load, you will notice that the data is coming straight from the cache and is not hitting the entity framework code at all. If you click the button to invalidate the cache, you will notice that the cache is now empty and that it has to go back to the database to update the cache.

Stale data is an issue when employing the use of any caching framework, and we will be dealing with this in two ways over the next two parts of this series: using the new SqlDependency wrapper, and also using a more code-centric technique in the third part. In the project source code I have included the full MVC project and the Sql file which allows you to install the demo database into your own Sql Server instance.

Download the project source code