jahu
jahu

Reputation: 5667

Store a null in System.Web.HttpContext.Current.Cache

In an MVC App we are suppose to dynamically load some strings from database. Basically a <string,string> key-value dictionary. Loading each text each time from the database would kill the application, so those texts would normally be pulled from the DB just once and then stored in cache (namely Web.HttpContext.Current.Cache). There are also default texts that are hardcoded and those would be used if nothing was found in the database. To make things more simple I would also put those default texts in the cache.

The logic is as follows:

  1. Try getting the text from cache and return it if it's found.

  2. If there was no text in the cache, try pulling it from the databaseand and if it's found, save it to cache and return it.

  3. If all above failed, use the default text and save it to the cache so no further queries will be made for this particular key.

If I understand it correctly the only way to check if key is set in Web.HttpContext.Current.Cache is by comparing it's value to a null. My problem is, it's not entirely unlikely that the default text will be a null. So if I put that in the cache, I will not know that the key was set and I will try to pull the non-existant value from the database each time the text is needed.

I know I could use an empty string instead of a null, but it's possible that I might need to distinguish between nulls and empty strings for some reason in the near future.

So is there some way to tell if a key is set in the Web.HttpContext.Current.Cache when the value assigned is a null?

Upvotes: 1

Views: 2283

Answers (3)

Jon Hanna
Jon Hanna

Reputation: 113342

If you must store a null in something that doesn't distinguish between nulls and key-not-found conditions, store an "active null" object to represent a null you've actually added:

private static readonly object ActiveNull = new object();

public bool GetIfPresent(string key, out object value)
{
    object fromCache = Web.HttpContext.Current.Cache.Get(key);
    if(fromCache == null)
    {
      //failed to obtain.
      value = null;
      return false;
    }
    if(ReferenceEquals(fromCache, ActiveNull))
    {
      //obtained value representing null.
      value = null;
      return true;
    }
    value = fromCache;
    return true;
}

public void AddToCache(string key, object value, CacheDependency dependencies, DateTime absoluteExpiration, TimeSpan slidingExpiration, CacheItemPriority priority, CacheItemRemovedCallback onRemoveCallback)
{
  Web.HttpContext.Current.Cache.Add(key, value ?? ActiveNull, dependencies, absoluteExpiration, slidingExpiration, priority, onRemoveCallback);
}

Upvotes: 1

StriplingWarrior
StriplingWarrior

Reputation: 156634

null is highly overused by most C# developers. If you recognize that these values are optional (meaning they may or may not be there), you may want to make your cache entries be of some type that wraps the actual type you're storing. That way, getting a null value means that there is no entry, whereas getting a non-null value that has a null Value property on it means it exists in the cache, but its value is null.

Incidentally, I've been working on a library to represent exactly this sort of wrapper. It's still in its alpha phases at the moment, but at least for this purpose it should be safe to use. You can get it from Nuget under the name "CallMeMaybe".

object result = HttpContext.Current.Cache.Get(key);
if(result != null)
{
    return ((Maybe<string>)result).Else(() => null);
}
var value = Maybe.From(GetValueFromDb());
HttpContext.Current.Cache.Add(key, value, ...);
return value;

Another option would be to use MemoryCache directly, which I think is what backs the HttpContext.Current.Cache these days, but which provides additional methods like GetCacheItem.

Upvotes: 2

Ryan Erdmann
Ryan Erdmann

Reputation: 1826

StriplingWarrior beat me to the punch, but I agree with what he said: just wrap what you're storing in a complex type so you can do a null check. Normally you'd use Nullable<T> for this, but you can't use a Nullable type for things that are already considered nullable, like strings.

Here's a practical example:

Define a simple wrapper class:

public class CacheEntry<T>
{
    public T Value { get; private set; }

    public CacheEntry(T value)
    {
        Value = value;
    } 
}

And then use it!

System.Web.HttpContext.Current.Cache.Insert("name", new CacheEntry<string>("Marcin"));

var name = (CacheEntry<string>) System.Web.HttpContext.Current.Cache.Get("name");
if (name != null)
{
    Console.WriteLine(name.Value);
}

Upvotes: 3

Related Questions