Easier way to manage your ASP.NET Cache

I was recently told by a client of mine that the ASP.NET app I developed was WAY TOO SLOW! I had to agree; the site was pinging the database twice on every load of the home page. So I said, "Give me a week... I'll make it work better". I went home feeling bad... my app was slow and I really didn't know where to begin. I had a lot of code that depended on pinging the database and I didn't want to sift through it all. What I ended up doing was using the cache object.

I started to look at other open source projects and their approaches to caching. Than DotNetKicks' cache manager caught my eye! I went off that idea and created my own way of implementing an object to interface with the cache object, making it more manageable.

The Cache manager I wrote has 3 methods (Grab, insert, clear), a constructor and 2 properties (CacheKey, CacheDuration). So here's my code in VB and C#:

 

using System.Web;
using System.Web.Caching;
namespace MainSite.Cache
{
  public class CacheManager<T>
  {
    private string m_cachekey = "";
    public string CacheKey {
      get { return m_cachekey; }
      set { m_cachekey = value; }
    }

    private int m_cacheduration;
    public int CacheDuration {
      get { return m_cacheduration; }
      set { m_cacheduration = value; }
    }

    public CacheManager(string Key, int duration)
    {
      this.CacheKey = Key;
      this.CacheDuration = duration;
    }

    public T Grab()
    {
      return (T)HttpContext.Current.Cache(this.CacheKey);
    }

    public void Insert(T obj, System.Web.Caching.CacheItemPriority priority)
    {
      DateTime expiration = DateTime.Now.AddMinutes(this.CacheDuration);
      HttpContext.Current.Cache.Add(this.CacheKey, obj, null, expiration, TimeSpan.Zero, priority, null);
    }

    public void Clear()
    {
      HttpContext.Current.Cache.Remove(this.CacheKey);
    }

  }
}

Imports System.Web
Imports System.Web.Caching

Namespace MainSite.Cache
    Public Class CacheManager(Of T)

        Private m_cachekey As String = ""
        Public Property CacheKey() As String
            Get
                Return m_cachekey
            End Get
            Set(ByVal value As String)
                m_cachekey = value
            End Set
        End Property

        Private m_cacheduration As Integer
        Public Property CacheDuration() As Integer
            Get
                Return m_cacheduration
            End Get
            Set(ByVal value As Integer)
                m_cacheduration = value
            End Set
        End Property

        Public Sub New(ByVal Key As String, ByVal duration As Integer)
            CacheKey() = Key
            CacheDuration() = duration
        End Sub

        Public Function Grab() As T
            Return CType(HttpContext.Current.Cache(CacheKey()), T)
        End Function

        Public Sub Insert(ByVal obj As T, ByVal priority As System.Web.Caching.CacheItemPriority)
            Dim expiration As DateTime = DateTime.Now.AddMinutes(CacheDuration())
            HttpContext.Current.Cache.Add(CacheKey(), obj, Nothing, expiration, TimeSpan.Zero, priority, Nothing)
        End Sub

        Public Sub Clear()
            HttpContext.Current.Cache.Remove(CacheKey())
        End Sub

    End Class
End Namespace

 

So what I do is create a class and have a grab method that makes an instance of the cache manager object and calls the cache manager class' grab method. If the cache returns null, I have a private method in my class that does the nesessary things to put the info into the cache object. Here's an example:

namespace MainSite.Cache
{
  public class ReviewsCache
  {
    public static ReviewsCollection Grab()
    {
      CacheManager<ReviewsCollection> man = new CacheManager<ReviewsCollection>(GetKey(), 90);

      ReviewsCollection cont = man.Grab();

      if (cont == null) cont = Insert(man); 

      return cont;
    }

    private static ReviewsCollection Insert(CacheManager<ReviewsCollection> man)
    {
      ReviewsCollection cont = MainSite.Logic.Reviews.GetAll();
      man.Insert(cont, Web.Caching.CacheItemPriority.Default);
      return cont;
    }

    public static void Delete(string sectionname)
    {
      CacheManager<ReviewsCollection> man = new CacheManager<ReviewsCollection>(GetKey(), 90);
      man.Clear();
    }

    private static string GetKey()
    {
      return "Reviews";
    }

  }
}
Namespace MainSite.Cache
    Public Class ReviewsCache

        Public Shared Function Grab() As ReviewsCollection
            Dim man As New CacheManager(Of ReviewsCollection)(GetKey(), 90)

            Dim cont As ReviewsCollection = man.Grab()

            If cont Is Nothing Then cont = Insert(man)

            Return cont
        End Function

        Private Shared Function Insert(ByVal man As CacheManager(Of ReviewsCollection)) As ReviewsCollection
            Dim cont As ReviewsCollection = MainSite.Logic.Reviews.GetAll()
            man.Insert(cont, Web.Caching.CacheItemPriority.Default)
            Return cont
        End Function

        Public Shared Sub Delete(ByVal sectionname As String)
            Dim man As New CacheManager(Of ReviewsCollection)(GetKey(), 90)
            man.Clear()
        End Sub

        Private Shared Function GetKey() As String
            Return "Reviews"
        End Function

    End Class
End Namespace

 

The cache object is a great thing to utilize in ASP.NET. But to make the caching manageable, you need to have a structure for managing the cache object and add logic to delete, grab, etc. Interfacing with a class is easier and follows the MVC pattern better than just saying Cache["MyKey"] in your logic or UI.



kick it on DotNetKicks.com
Published Saturday, October 20, 2007 2:11 PM by zowens
Filed under: , , ,

Comments

# Easier way to manage your ASP.NET Cache

Saturday, October 20, 2007 2:12 PM by DotNetKicks.com

You've been kicked (a good thing) - Trackback from DotNetKicks.com

# re: Easier way to manage your ASP.NET Cache

Saturday, October 20, 2007 2:20 PM by Joe Chung

WTF does your code have to do with MVC?  It looks like Identity Map to me.

# re: Easier way to manage your ASP.NET Cache

Saturday, October 20, 2007 7:54 PM by zowens

Joe,

It had a model (CacheManager) and controllers that use the CacheManager. It's sorta like MVC... not really... but I like to think of it that way.

# re: Easier way to manage your ASP.NET Cache

Sunday, October 21, 2007 9:12 AM by Chuck Conway

This has potential in-rush issues in a web farm scenerio. Refactoring your ReviewCache class to use locks and class variables would solve the problem.

# re: Easier way to manage your ASP.NET Cache

Sunday, October 21, 2007 10:48 AM by zowens

@Chuck

Yea... in my clients site, I did use locks. I wanted to keep it simple for this blog post though.

# re: Easier way to manage your ASP.NET Cache

Sunday, October 21, 2007 4:11 PM by JV

The biggest problem with cache usage is indeed that when using webfarms you can not store user dependant data. It actually should never store such information. However it can be very usefull in lots of other cases to increase performance drastically.

A second note: I agree with Joe. This manager has nothing to do with MVC. Yes, you can use it in a MVC application, but let's be fair, you can use about a lot of stuff inside a mvc application...

# re: Easier way to manage your ASP.NET Cache

Sunday, October 21, 2007 5:30 PM by zowens

OK... who uses a webfarm other than enterprises? MOST people use a shared or 1 dedicates/virtual server. The Cache object is optimal for those people. If you want a webfarm sort of caching scenario, USE ENTERPRISE LIBRARY!

I KNOW THIS HAS NOTHING TO DO WITH MVC!!! BUT THIS FOLLOWS THE PATTERN LOOSLY! I should have chosen my words better... whatever... give it a rest people.

# re: Easier way to manage your ASP.NET Cache

Sunday, October 21, 2007 6:03 PM by JV

Don't get me wrong Zack. Your manager is definatly very valuable for cache abstraction. I actually use a very similair implementation for it.

# re: Easier way to manage your ASP.NET Cache

Monday, October 22, 2007 1:44 PM by Mike Duncan

Hey um, give the dude a break already!  I don't know this guy from Adam, but it's pretty clear he's trying to help people out with the fruit of his labor, no need to get all Britney Spears catty on him over scenarios he never claimed to address or semantics.  I'm sure this code is very useful for some folks and a good starting place for others.

Keep it up man.  

# re: Easier way to manage your ASP.NET Cache

Monday, October 22, 2007 3:15 PM by Dennis

I have to agree with Mike. There are way too many people who read blogs just to blast whatever the author puts on up in an effort to assist. I wish there was  way to enforce the old saying; "If you don't have anything good to say, then just do say anything". Just my 2 cents.

# re: Easier way to manage your ASP.NET Cache

Monday, October 22, 2007 6:02 PM by Simone Busoli

Is this from Gavin or Zack? Anyways, I like the wrapper, but I'd prefer a more OO approach, with interfaces and implementors, it would be cleaner.

# re: Easier way to manage your ASP.NET Cache

Monday, October 22, 2007 6:06 PM by zowens

@Simone

This would be written by me, inspired from Gavin.

I could have use interfaces... but we should keep it simple!

# Hide and seek with the ASP.NET cache &lt; Trying This Again

Tuesday, November 13, 2007 12:27 AM by Hide and seek with the ASP.NET cache < Trying This Again

Pingback from  Hide and seek with the ASP.NET cache &lt; Trying This Again

# re: Easier way to manage your ASP.NET Cache

Saturday, January 05, 2008 1:35 PM by Shawn

Great article.  I stumbled on it after impl'ing my own and realizing that someone has probably already done this.  I added one nicety to mine that I don't see here.  A delegate called "OnGetNewData" for the cachemgr that will fire when the internal cache returns NULL for a lookup.  This allows the caller to subscribe to the event and handle the actual datafetch, then return back to the cachemgr for proper storage.

# re: Easier way to manage your ASP.NET Cache

Friday, June 27, 2008 1:58 AM by WhiteSites

Zach.  I know where you are coming from.  You learn something new,  create a class to make integration easy, then you tell the world about it.  Only to have people tell you that its old stuff.  You have good intentions.  Keep it up!  I remembered when I discovered the asp.net Cache Object.  It was like I discovered a new religion, very exciting times.  Your coding skills are a bit advanced to mine.  I tend to just stick to simple functions.  Here is how I do stuff in my helper functions.

<pre><code>

// add item to cache

public void addToCache(string cachename, string data, int hours){

Cache.Insert(cachename, data, null, DateTime.Now.AddHours(hours), System.Web.Caching.Cache.NoSlidingExpiration);

}

// get item from cache

public string getFromCache(string cachename){

return (string)Cache[cachename];

}

// in action with another helper function

public string getBlogName(string blogid){

string cachename="getBlogName_"+blogid;

if(getFromCache(cachename)!=null){return getFromCache(cachename);}

string data=countDB3("SELECT title FROM blogs where id='"+blogid+"'");

addToCache(cachename, data, 12);

return data;

}

</code></pre>

# re: Easier way to manage your ASP.NET Cache

Wednesday, September 24, 2008 4:43 PM by Dave Neeley

This was EXTREMELY helpful to me today!

# re: Easier way to manage your ASP.NET Cache

Wednesday, September 24, 2008 4:52 PM by Zack

@Dave

YAY! That's awesome!

# re: Easier way to manage your ASP.NET Cache

Monday, September 29, 2008 12:56 PM by Paul Jones

Hi Zack,

Congrats for such an informative article. I too have a blog regarding ASP.NET Cache, it various uses, limitations and how to fix them. You are absolutely correct. Caching is an awesome way of speeding up apps.

# re: Easier way to manage your ASP.NET Cache

Wednesday, October 22, 2008 4:39 PM by Code Dependent

This is a concise, informative article.  It turned up in my search for Cache information and convinced me to implement such a class in my project.  I'll be editing it somewhat to give the option of passing a CacheDependency object.  Thanks for sharing!

# re: Easier way to manage your ASP.NET Cache

Wednesday, October 29, 2008 12:29 PM by Sarah on ASP.NET Cache

Hi Zack!

Nice post.

I must point out however that ASP.NET Cache has some very intrinsic problems. Its not scalable for large server farms and its In-Process nature isn't reliable either.The right way to solve this scalability problem is through an in-memory distributed cache.

Microsoft is finally realizing it. They're working on Velocity but that is still in its infancy and will take some time to stabilize and mature.

# re: Easier way to manage your ASP.NET Cache

Wednesday, November 26, 2008 3:47 PM by Eric

Thanks for the awesome code sample!

I found one minor thing wrong with your C# example - you have to use brackets and not parens for accessing the cache like "HttpContext.Current.Cache[this.CacheKey]" instead of "HttpContext.Current.Cache(this.CacheKey)".

Also, I was getting some NullReferenceExceptions in that method, so I modified it like so and it works nicely:

  public T Grab()

  {

     T temp;

     try

     {

        temp = (T)HttpContext.Current.Cache[this.CacheKey];

     }

     catch (NullReferenceException)

     {

        temp = default(T);

     }

        return temp;

  }

# re: Easier way to manage your ASP.NET Cache

Wednesday, November 26, 2008 4:25 PM by zowens

@Eric

Yea... i was converting from VB to C# and didn't catch that. I'm not that much of a noob anymore :)

I disagree with you on the NullReferenceException. If the value is null in the cache, why return something? The manager class is just an API that delegates the functionality of the HttpCache.

# re: Easier way to manage your ASP.NET Cache

Wednesday, November 26, 2008 8:33 PM by Eric

Well, you can feel free to disagree, but I had to do something because the exception was crashing my app :)  For me this works, as I can test for the returned value safely now...

# re: Easier way to manage your ASP.NET Cache

Wednesday, November 26, 2008 9:12 PM by Zack Owens

@Eric

Just sayin that its not necessarily wrong to return null. Guess its a choice rather a black and white situation.

Leave a Comment

(required) 
(required) 
(optional)
(required)