Null as value in HttpRuntime.Cache.Add
Asked Answered
G

4

5

I want to store null for some of the keys in HttpRuntime.Cache as I dont want to again go to Database to find that there is no entry for that key.

So first time, it goes to database and fills the cache. The intent is to serve the following calls using cached data instead of doing the database call.

Here is the code that I am using the following:

            Info info = null;
        if (HttpRuntime.Cache["Info_" + id.ToString() + "_" + quantity.ToString()] != null)
            info = HttpRuntime.Cache["Info_" + id.ToString() + "_" + quantity.ToString()] as Info;
        if (info == null)
        {
            info = (from dd in dc.Infos
                              where dd.id == id && dd.active == true && dd.quantitytooffset == quantity
                              select dd).SingleOrDefault();
            HttpRuntime.Cache.Add("Info_" + id.ToString() + "_" + quantity.ToString(), info, null, System.Web.Caching.Cache.NoAbsoluteExpiration, System.Web.Caching.Cache.NoSlidingExpiration, System.Web.Caching.CacheItemPriority.High, null);
        }

The last line of code i.e. HttpRuntime.Cache.Add throws an System.ArgumentNullException: Value cannot be null.

Any idea if this is possible or I need to use other datastructure to store null values and look up later?

Gaullist answered 22/3, 2012 at 10:39 Comment(1)
Check out this post: #968393Pylle
A
6

You could use your own "null" value to place into cache. For example,

private static Info NULL_INFO = new Info();

Then you could use that instead of null in HttpRuntime.Cache.Add and later after retrieval from cache check that you didn't get your NULL_INFO

if ( info == NULL_INFO) // if you don't have equality operator overloaded, otherwise you'd better use ReferenceEquals() 
    // return empty data
else if (info == null)
    // proceed with try to load from database
Accentuate answered 22/3, 2012 at 10:55 Comment(3)
I would use a static object here. Seems to be a good option. Let me see if there are any other methods.Gaullist
This is the null object patternLicence
This can also be resolved using a generic wrapper class https://mcmap.net/q/558700/-asp-net-can-39-t-cache-null-value . I like both solutions.Thirtytwo
L
1

I wrote a blog post recently about how the null keyword is often abused leading to this kind of confusion. In your particular case I would look at using an option type to indicate the lack or presence of data rather than null.

I have a simple implementation of an Option type you can use here

The usage would then be something like:

if (HttpRuntime.Cache["xyz"] == null)
// Impossible to make a distinction between whether or not the cache value is missing
// or if it is present but explicitly a null value...

HttpRuntime.Cache["xyz"] = Option<String>.None();
// We have now explicitly stated that the cache contains xyz but the value is null...

HttpRuntime.Cache["xyz"] = Option<String>.Some("hello world");

if (HttpRuntime.Cache["xyz"].IsSome)
{
    // cache contains a non-null value for xyz...
}
Licence answered 22/3, 2012 at 11:8 Comment(0)
J
0

You just need to check whether the value obtained from your data source is not null before trying to add it back to the cache, see below:

        Info info = null;
    if (HttpRuntime.Cache["Info_" + id.ToString() + "_" + quantity.ToString()] != null)
        info = HttpRuntime.Cache["Info_" + id.ToString() + "_" + quantity.ToString()] as Info;
    if (info == null)
    {
        info = (from dd in dc.Infos
                          where dd.id == id && dd.active == true && dd.quantitytooffset == quantity
                          select dd).SingleOrDefault();
        if (info != null)
        {
            HttpRuntime.Cache.Add("Info_" + id.ToString() + "_" + quantity.ToString(), info, null, System.Web.Caching.Cache.NoAbsoluteExpiration, System.Web.Caching.Cache.NoSlidingExpiration, System.Web.Caching.CacheItemPriority.High, null);
        }
    }

As you are using the as Info conversion at the beginning of your code, if the key isn't present in the cache it'll return a null value anyway, so you don't need to store the null value in cache. Storing null values in the cache doesn't really serve any use, so there's a reason the framework is not allowing you to do that.

Also as a slight side-note, it would be good to create your cache key once, then re-use it rather than reconstructing it every time it's used. For instance:

var key = string.Format("Info_{0}_{1}", id, quantity);

Then just use:

HttpRuntime.Cache[key]

When accessing it, it'll make your code less prone to typo errors.

Jasun answered 22/3, 2012 at 11:22 Comment(0)
L
0

This is a generic static class/method to solve this quite common problem (null which means "i checked and it doesn't exist, I don't need to check again").

This solution wraps the value like in the other answers.

Usage example

    var user = await Cached.Get(
          _cache, userName, 
          async () => _dbContext.LoadAsync<DbUser>(userName)));

Implementation

    public class CachedEntry<T>
    {
        public CachedEntry(T value)
        {
            Value = value;
        }

        public T Value { get; }
    }

    public static async Task<T> Get<T>(IMemoryCache cache, 
    string key, Func<Task<T>> getDelegate)
    {
        if (key == null)
        {
            throw new ArgumentNullException(nameof(key));
        }

        var cachedEntry = cache.Get<CachedEntry<T>>(key);
        if (cachedEntry == null)
        {
           var result = await getDelegate();

           cachedEntry = new CachedEntry<T>(result);
           cache.Set(key, cachedEntry);
        }

        return cachedEntry.Value;
    }

Lullaby answered 14/10, 2019 at 0:31 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.