Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Most Efficient Way Of Clearing Cache Using ASP.NET

I am building an ASP.NET/Umbraco powered website which is very custom data driven via entity framework, we are having to cache quite a lot of the data queries (For example searches by keyword) as it's a busy site.

But when a user creates a new data entry, I need to clear all the cached queries (Searches etc..) so the new entry is available in the results.

So in my create, delete and update methods I am calling the following method:

public static void ClearCacheItems()
{
    var enumerator = HttpContext.Current.Cache.GetEnumerator();

    while (enumerator.MoveNext())
    {
        HttpContext.Current.Cache.Remove(enumerator.Key.ToString());
    }
}

Is this really bad? I can't see how else I am supposed to clear the cached items?

like image 689
YodasMyDad Avatar asked Jun 02 '11 09:06

YodasMyDad


People also ask

How to cache data in ASP NET MVC?

Always use caching with images or media files. Generally, we can cache our data using 3 ways in ASP.NET MVC. Caching whole or partial page response using OutputCache Attribute While designing any website, we have to use static content.

Why doesn't ASP NET cache the entire page?

Because what is appropriate for one version of ASP.NET may not be appropriate for newer versions. Also, this answer doesn't give you any control over the caching policy. Instead, it's a "one shot kill" for the entire page. What's more common is to cache the page, but not certain items on the page, like data grids.

How to remove static content from cache in ASP NET Framework?

In asp.net framework, there are some provision to set cache expiry date for static content in web config file, we can set a specific date or we can set max age as cache expiry date for static, we to remove content from cache you can simply make changes in following tags in web.config file.

How do I clear the cache?

My advice would be to design the cache in a way that clearing the cache would be very easy. For example group your cache items (create a class to hold related cache results) by an ID and use the ID as the key. Whenever something related to that ID is changed, clear the cache for that ID. Easy, peasy.


2 Answers

The method you use is actually the correct way to clear your cache, there is just one minor 'error' in your code. The enumerator only is valid as long as the original collection remains unchanged. So while the code might work most of the time, there might be small errors in certain situations. Best is to use the following code, which does essentially the same, but does not use the enumerator directly.

List<string> keys = new List<string>();
IDictionaryEnumerator enumerator = Cache.GetEnumerator();

while (enumerator.MoveNext())
  keys.Add(enumerator.Key.ToString());

for (int i = 0; i < keys.Count; i++)
  Cache.Remove(keys[i]);
like image 104
KilZone Avatar answered Sep 21 '22 16:09

KilZone


Clearing the whole ASP.NET cache for just one specific functional domain seems a bit overkill.

You could create an intermediate object, and store all your cached queries in there. This object could be just a wrapper on a dictionary object. All queries should use this object instead of playing with the ASP.NET cache directly.

Then you add this object to the ASP.NET Cache when you need it. When you need to clear the queries, just get to this object and clear the underlying dictionary. Here is a sample implentation:

    public sealed class IntermediateCache<T>
    {
        private Dictionary<string, T> _dictionary = new Dictionary<string, T>();

        private IntermediateCache()
        {
        }

        public static IntermediateCache<T> Current
        {
            get
            {
                string key = "IntermediateCache|" + typeof(T).FullName;
                IntermediateCache<T> current = HttpContext.Current.Cache[key] as IntermediateCache<T>;
                if (current == null)
                {
                    current = new IntermediateCache<T>();
                    HttpContext.Current.Cache[key] = current;
                }
                return current;
            }
        }

        public T Get(string key, T defaultValue)
        {
            if (key == null)
                throw new ArgumentNullException("key");

            T value;
            if (_dictionary.TryGetValue(key, out value))
                return value;

            return defaultValue;
        }

        public void Set(string key, T value)
        {
            if (key == null)
                throw new ArgumentNullException("key");

            _dictionary[key] = value;
        }

        public void Clear()
        {
            _dictionary.Clear();
        }
    }

If my query is represented like this:

    public class MyQueryObject
    {
       ....
    }

Then, I would use the "regional" cache like this:

// put something in this intermediate cache
IntermediateCache<MyQueryObject>.Current.Set("myKey", myObj);

// clear this cache
IntermediateCache<MyQueryObject>.Current.Clear();
like image 24
Simon Mourier Avatar answered Sep 21 '22 16:09

Simon Mourier