{"id":38451,"date":"2020-08-05T20:00:22","date_gmt":"2020-08-05T19:00:22","guid":{"rendered":""},"modified":"2020-08-06T18:28:37","modified_gmt":"2020-08-06T17:28:37","slug":"inversion-of-control-cached-proxy-pattern","status":"publish","type":"post","link":"https:\/\/www.microsoft.com\/en-gb\/industry\/blog\/technetuk\/2020\/08\/05\/inversion-of-control-cached-proxy-pattern\/","title":{"rendered":"Inversion of Control Cached Proxy Pattern"},"content":{"rendered":"

\"An<\/p>\n

For a developer, there are fewer things more satisfying than an \u201caha\u201d moment when they find exactly where a performance bottleneck sits.<\/p>\n

When part of an application is not performing well, we often turn to cache as a solution. We grab our nearest caching library, then wrap the under-performing code in cache. Mission accomplished with only a single if-statement. We then bask in the genius of our problem solving skills. \u201cIt\u2019s such a simple solution\u201d, we proclaim!<\/p>\n

So we sprinkle this caching code (if-statement) all around our solution like little bits of delicious grated cheese on a pizza. Then one day you hear reports of unreplicatable intermittent performance problems, stale content\/settings, and even disappearing values. Like grated cheese on a pizza, once a cache is baked in, you will find it very hard to unpick.<\/p>\n

In this article we present a caching pattern which avoids common caching pitfalls, by decoupling the cache from the unperforming code using inversion of control. The examples will be given in c#, however, the caching technique is universally applicable.<\/p>\n

 <\/p>\n

A Common Cache Pattern<\/h2>\n

One of the most common ways to implement cache is via wrapping under-performing code in an if-statement. This technique means that you are essentially baking the cache into the solution. It can be very hard (and messy) to disable this cache if you need to diagnose issues.<\/p>\n

Let\u2019s start with an initial problem! The following is some code which has a performance bottleneck.<\/p>\n

public MyModel GetById(int id)\r\n    {\r\n        var value = GetValue(id); \/\/ do something expensive\r\n\r\n        return value;\r\n    }<\/pre>\n

A common approach would be to wrap the expensive part of the method with some cache. Here is that same method using cache. It will probably look very familiar.<\/p>\n

public MyModel GetById(int id)\r\n    {\r\n        var cacheKey = \u201cmyCachePrefix-\u201d + id;\r\n\r\n        var value = _cache.Get(cacheKey) as MyModel;\r\n        If (value == null)\r\n        {\r\n            value = GetValue(id); \/\/ do something expensive\r\n\r\n            _cache.Add(cacheKey, value, 60);\r\n        }\r\n\r\n        return value;\r\n    }<\/pre>\n

While many people may go to production with the code above, the more astute and cautious people will want to move that hardcoded 60sec value to some sort of AppSetting so they can tweak the cache time easily (perhaps on production).<\/p>\n

So then you have something like this:<\/p>\n

    public MyModel GetById(int id)\r\n    {\r\n        var cacheKey = \u201cmyCachePrefix-\u201d + id;\r\n\r\n        var value = _cache.Get(cacheKey) as MyModel;\r\n        if (value == null)\r\n        {\r\n            value = GetValue(id); \/\/ do something expensive\r\n\r\n            int cacheTime = int.Parse(ConfigurationManager.AppSettings[\u201cmyCacheTime\u201d]);\r\n            _cache.Add(cacheKey, value, cacheTime);\r\n        }\r\n\r\n        return value;\r\n    }<\/pre>\n

To clean this up, you may decide to pass a function into the cache, so that the cache library will call it automatically. This is a nice way to remove that if statement from your code.<\/p>\n

public MyModel GetById(int id)\r\n{\r\n    var cacheKey = \u201cmyCachePrefix-\u201d + id;\r\n    int cacheTime = int.Parse(ConfigurationManager.AppSettings[\u201cmyCacheTime\u201d]);\r\n\r\n    return _cache.Get(cacheKey, () =>\r\n    {\r\n        return GetValue(id);\r\n    }, cacheTime) as MyModel;\r\n}<\/pre>\n

Some Issues<\/h3>\n

The first observation is that this code is arguably no longer following SOLID<\/a> principles. It doesn\u2019t just get the value anymore. It also caches it. The second observation is that the only way to disable the cache is to set the cache time to zero. Arguably, this is not even really \u201cdisabling\u201d the cache because the cache class is still being used.<\/p>\n

When diagnosing issues the questions you should ask are:<\/p>\n