Wednesday, 8 October 2008

Microsoft Enterprise Library #4 – Cache Application Block

At first glance there wasn't anything useful in the Cache Application Block. As we delved further into the requirements of our project I've found that there are many things that we would like to keep at hand and not have go delve off into the database to retrieve. But is the Cache application block worth my time to use? If it saves on retrieval time for data objects that I would like to keep handy then it is definitely worth while. Even it it removes the need to perform a transaction against the database that will be heavily utilised it will be worth while.

My Requirements

For my Requirements I have a very simple task.

  1. Cache the user that I've loaded from the database using the Entity Framework
  2. Reload that user at any time
  3. Expire the user after 60 seconds
  4. Perform a particular action when the user's cache expires

I could use membership to do a similar task, but if I'm not using membership (like if I use Windows authentication), then this may be useful. There are lots of other ways to do this I'm aware, but this will show off the cache application block using a simple scenario.

Step 1 - Setup the block

I added the DLL Files that I needed. Again Vikas Goyal has a good tutorial on this one here and I used this to help setup the config files that I need and get an idea on how the block works. I used an ASP.NET MVC project as that is the tool we will be going with, but you don't need to use one if you prefer something else. Next I opened the web.config (or app.config in winforms) with the enterprise library config tool and added the config application block to my project. I want to cache the logged in user's details so I added a new cache manager and called it UserManager. I set the poll frequency to 10 seconds because I want my timeout to be 60 seconds or there about but left the rest as default. I should be done now.

Step 2 - Create My Cache Object

I've got my simple class:

namespace EntLibCABDemo.Models
{
 public class UserDetails
 {
    public String UserName { get; set; }
    public String State { get; set; }
    public String EmailAddress { get; set; }
 }
}

Step 3 - Implement the Cache

So now I'm going to create a new action for viewing the data in the cache. If the action doesn't find the data in the cache it will display a message telling me that it has re-created it, otherwise it will display the data. If I refresh the page after 60 seconds it should let me know it has re-created it. I created a CreateUser action to create the user with the code below. The SlidingTime specifies that every time I access the object that it will restart the timer. Once created, we'll redirect to the view method.

public ActionResult CreateUser()
{
 Models.UserDetails user = new Models.UserDetails();
 user.UserName = "Steve";
 user.EmailAddress = "Steve@company.com";
 user.State = "NSW";
 
 ICacheManager userCache = CacheFactory.GetCacheManager("UserManager");
 userCache.Add(user.UserName, user, CacheItemPriority.Normal,
     null, new SlidingTime(TimeSpan.FromSeconds(60)));
 
 return Redirect("ViewUser");
}

And then a ViewUser to view the user. Here we get the user from the cache and setup the view data with a string to display.

public ActionResult ViewUser()
{
 ICacheManager userCache = CacheFactory.GetCacheManager("UserManager");
 Models.UserDetails user = (Models.UserDetails)userCache.GetData("Steve");
 
 if (user == null)
 {
     // Not found, tell the user.
     ViewData["Message"] = "No current user found for 'Steve'";
 }
 else
 {
     // Found, set the string.
     ViewData["Message"] = "Found Steve: " + user.State + " " + user.EmailAddress;
 }
 
 return View("User");
}

Then the view to display the user, just getting the view data and spitting it out to the screen.

Details: <%=Html.Encode(ViewData["Message"])%>

And I'm ready to test, Lo and behold it works. Hitting the create method will return the view and show the user's details "Found Steve: NSW Steve@company.com". If I leave it 60 seconds and refresh the view I get "No current user found for 'Steve'". If I refresh before the 60 seconds is up the cache restarts the timeout and I get another 60 seconds of time.

Step 4 - Handling Events on Expiration

Now I want to handle an event when the content expires. All I need to do is create a class that implements the ICacheItemRefreshAction interface and set it when I add the item. To test it, when the user expires I'll change the email address and reset it in the cache.

public class RefreshCache : ICacheItemRefreshAction
{
  public void Refresh(string removedKey, object expiredValue,
      CacheItemRemovedReason removalReason)
  {
      UserDetails user = (UserDetails)expiredValue;
      user.EmailAddress = "new@company.com";
      CacheFactory.GetCacheManager("UserManager").Add(removedKey, user);
  }
}

That's it. When testing, after 60 seconds the cache expired and fired the event. The user was set back into the cache and when I refreshed the page the email address changed from Steve@company.com to new@company.com.

Final Thoughts

The Cache Application Block is very simple and works very well from all my first tests. I can't think of a reason not to use it. We will be using it to cache items from the database and file system information that we don't want to continually reload.

1 comment:

William said...

CAB is a wonderful tool as far as Performance is concern. But it has some limitation as well. Especially due to its stand alone behavior it is unable to resolve issues like scalability and Reliability. So in order to overcome this issue one has to go for third party caching solution. I have a very informative read about The Drawbacks of using Regular Caching Application Block. One should also study it before going ahead with CAB.