3
\$\begingroup\$

I am trying to ensure that (once a cache item is populated) even if it's refresh object takes a long time to retrieve, the original cache item can be requested.

using System;
using System.Runtime.Caching;

namespace LongCacheTest
{
    class Program
    {
        static void Main(string[] args)
        {
            Cacher.Add("key", "object", Get);

            Console.ReadLine();
        }

        public static Object Get()
        {
            // load big file from disck
            return "stuff";
        }
    }

    public class Cacheable
    {
        public delegate Object CacheEntryGet();

        public Object Obj { get; set; }
        public CacheEntryGet Getter { get; set; }
    }

    public class Cacher
    {
        private static MemoryCache _cache = null;

        private static MemoryCache Cache => _cache = _cache ?? new MemoryCache("long");

        private static CacheItemPolicy Policy => new CacheItemPolicy
        {
            AbsoluteExpiration = new DateTimeOffset(DateTime.Now.AddHours(1)),
            Priority = CacheItemPriority.Default,
            RemovedCallback = DidRemove
        };

        public static void DidRemove(CacheEntryRemovedArguments args)
        {
            Cache.Add(args.CacheItem.Key, args.CacheItem.Value, Policy);

            var item = args.CacheItem.Value as Cacheable;

            item.Obj = item.Getter();

            Cache.Remove(args.CacheItem.Key);
            Cache.Add(args.CacheItem.Key, item, Policy);
        }

        public static void Add(string key, Object obj, Cacheable.CacheEntryGet getter)
        {
            var item = new Cacheable{
                Obj = obj,
                Getter = getter
            };

            Cache.Add(key, item, Policy);
        }
    }
}

The practice here is to provide a method Getter and cache it alongside the cached Object. When the cache item expires, it is reinserted and the Getter method is called to get the refresh object. Once the fresh object is retrieved it replaces the original cache item.

\$\endgroup\$

1 Answer 1

3
\$\begingroup\$

How do I use this?

I don't understand how to use this. It took me some time to understand the reason for the getter, and I can't see how to retrieve anything from this cache.

Why do I need to Add the value when I have the delegate?

In the Add method, it doesn't make sense to add provide both a value and a method that calculates the value; it is very likely that someone will pass in inconsistent arguments and get unexpected results. It should suffice to simply pass in the delegate, and let the value calculate itself.

Why is this static?

It is very dangerous to have one global cache holding every different type. You should allow for multiple caches for different purposes, and then you can...

Make this cache generic

Nobody likes to cast from Object. Your interface should be Cacheable<T>, so that we can check at compile time that the type to be returned is what we expect.

Your response is not guaranteed

Your callback is only called after the item has been removed from the cache. Even if you add the previous value back straight away, a poorly-timed interrupt could have another thread searching for a key in that split-millisecond it's not there.

\$\endgroup\$
2
  • \$\begingroup\$ Thanks for your help! If I wanted to post the revised code, should it be as an updated to the original post or a new post? \$\endgroup\$
    – Matt W
    Commented Jan 23, 2019 at 5:35
  • 1
    \$\begingroup\$ @MattW Refer to this meta post for the acceptable options \$\endgroup\$ Commented Jan 23, 2019 at 19:33

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.