2
\$\begingroup\$

Wrapper:

internal static class Cache
{
    private static ObjectCache InternalCache
    {
        get
        {
            return MemoryCache.Default;
        }
    }

    private static T CacheOrGetExisting<T>(string key, Func<T> valueFactory)
    {
        T value;
        object uncastedValue = InternalCache.Get(key);

        if (uncastedValue == null)
        {
            value = valueFactory();
            InternalCache.Set(key, value, DateTimeOffset.UtcNow.AddHours(1));
        }
        else
        {
            value = (T)uncastedValue;
        }

        return value;
    }

    public static decimal Foo
    {
        get
        {
            return CacheOrGetExisting("foo", () =>
                {
                    return 5m; // data access here
                });
        }
    }

    public static Bar Bar
    {
        get
        {
            return CacheOrGetExisting("bar", () =>
                {
                    return new Bar(); // data access here
                });
        }
    }
}

Usage in a Controller:

model.Foo = Cache.Foo;

One thing I'm not sure of: is the readonly property necessary for InternalCache or can I write private static ObjectCache InternalCache = MemoryCache.Default;?

I'm not worried about the tiny chance of multiple calls to the same uncached object causing multiple value = valueFactory(); and I'm also not worried about stale data. The main goal is to cache some informational data that is simply displayed on the screen, instead of retrieving it every request.

I've tested this with an expiration of 1 minute and a class that gets DateTime.Now on creation, and it worked fine, so I believe the code is bug-free.

\$\endgroup\$
  • 1
    \$\begingroup\$ I'd start getting worried long before I had this many statics in such a small area. \$\endgroup\$ – Magus Jun 18 '14 at 14:21
  • \$\begingroup\$ @Magus what would I gain from making Cache non-static? There's no state to be held here, just reads and writes to MemoryCache.Default (which is static because that's how you do in-memory caching in a web context). \$\endgroup\$ – Stijn Jun 18 '14 at 14:27
2
\$\begingroup\$

As Magus pointed out in the comments, making everything static can present some issues. Using these static properties in other classes makes those classes tightly coupled to the Cache class. Why should you care? Consider this class:

public class Bar
{
    public int DoSomething()
    {
        return 5 + Cache.CachedNumber;
    }
}

You've lost your ability to test DoSomething() in isolation from the rest of your code. Any time you test DoSomething(), it relies on a specific implementation of Cache. It also makes it difficult to use the Bar class with different cache types (in memory, database, file system, etc.). Ideally, Bar would look something like this:

public class Bar
{
    private ICacheProvider Cache { get; set; }

    public Bar(ICacheProvider cache)
    {
        this.Cache = cache;
    }

    public int DoSomething()
    {
        return 5 + Cache.CachedNumber;
    }
}

Now we can:

  1. Test that DoSomething is doing its job, regardless of flaws in the ICacheProvider instance (by passing a mock ICacheProvider that always returns CachedNumber = 3, for example).
  2. Pass in different ICacheProvider implementations that can very depending on my use case, and Bar will still work without caring about them.

Also, I would suggest not using something as vague as Cache or ICacheProvider (used for illustration). Once you start caching a lot of things, you may want to cache different properties differently, at which point your super cache class will become too large to maintain. Consider breaking it up into meaningful repositories that can implement caching however they want, or something similar.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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