Question

I've created my own cache manager for a web site I'm developing and I was looking to find the best way to clear the cache under certain circumstances.

I found many articles saying the proper way to clear the cache is to call HttpRuntime.Close()

However, in my unit tests setup I call the encapsulated function HttpRuntime.Close() and the cache is NOT being cleared out.

I expected it to perform something similar to

foreach (DictionaryEntry cacheItem in HttpRuntime.Cache)
{
    HttpRuntime.Cache.Remove(cacheItem.Key.ToString());
}

The foreach loop works great in my encapsulated function, but the Close() never works right.

Am I misunderstanding the purpose of HttpRuntime.Close() or is there something more sinister going on here?

Was it helpful?

Solution

Don't use Close, it does more than the docs say. And the docs also say not to use it while processing normal requests...

This is the reflected source of Close():

[SecurityPermission(SecurityAction.Demand, Unrestricted=true)]
public static void Close() {
    if (_theRuntime.InitiateShutdownOnce()) {
        SetShutdownReason(ApplicationShutdownReason.HttpRuntimeClose, "HttpRuntime.Close is called");
        if (HostingEnvironment.IsHosted) {
            HostingEnvironment.InitiateShutdown();
        } else {
            _theRuntime.Dispose();
        }
    }
}

Also, you cannot iterate over a collection and remove items from it at the same time, as this renders the enumeration invalid.

So, try this instead, which doesn't change what it loops over:

List<string> toRemove = new List<string>();
foreach (DictionaryEntry cacheItem in HttpRuntime.Cache) {
    toRemove.Add(cacheItem.Key.ToString());
}
foreach (string key in toRemove) {
    HttpRuntime.Cache.Remove(key);
}

That being said, really, you should try to use cache dependencies to have the invalid cache entries cleared automatically for you, and then all this becomes unnecessary.

OTHER TIPS

I understand the issue with enumeration but for some reason the Cache doesn't seem to have a problem removing an item while walking through the list.

If you drill down to the detail implementation, you will find the Enumerator is created by CacheSingle.CreateEnumerator, a new Hashtable instance is created for enumeration.

That's why you can do the remove in a foreach loop.

you could simply implement your own Cache class, check the below one:

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

    private YourCache()
    {
    }

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

    public T Get(string key, T defaultValue)
    {
        if (string.IsNullOrWhiteSpace(key))
            throw new ArgumentNullException("key should not be NULL");

        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();
    }
}

you could call items from cache or even clear them using the following:

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

// clear this cache
YourCache<ClassObject>.Current.Clear();
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top