November Happy Hour will be moved to Thursday December 5th.

Daniel Ovaska
Jan 10, 2017
  2336
(4 votes)

Nuget package for Caching interceptor

Want to speed up your website? Caching external calls and database calls is usually a great way to start. Writing caching code gets pretty boring and messy if you have plenty of repositories you want to cache. So if you want to keep your code both DRY and SOLID, using interceptors can be a great way. If you missed my earlier blog post about caching and AOP you can read it here. The caching interceptor uses Episervers default cache behind the scene so should work with load balancing etc. 

To demonstrate how to use it I'm using a fake repository for news with a few methods that implements an interface. Imagine this repository is really slow and gets data from another site.

public interface INewsRepository
{
        IEnumerable<NewsItem> GetAllNews();
        IEnumerable<NewsItem> GetNews(string filter);
        GetNewsResponse GetNews(GetNewsRequest request);
}

Basic caching with interceptors

Let's add some caching to it. 

1. Install nuget package for caching interceptor from Episerver Nuget feed here. Episerver 10+ required.
Bribe me with a beer if you want it for earlier versions...

2. Register what class you want to use it on in IoC configuration like this:

container.For<INewsRepository>().Use<NewsRepository>();
container.RegisterInterceptor<INewsRepository>(new CacheInterceptor());

3. Decorate the interface with Cache attribute for the methods you want to cache

public interface INewsRepository
{
        [Cache(20, "News")] //Cache this method for 20s with "News" as master key. 
        IEnumerable<NewsItem> GetAllNews();
        [Cache(10, "News")] //Cache this method for 10s with "News" as master key. 
        IEnumerable<NewsItem> GetNews(string filter);
        [Cache] //Cache this method and use custom request and response class to control caching in detail. 
        GetNewsResponse GetNews(GetNewsRequest request);

}

And you are done! Now try to get news...

var newsRepo = ServiceLocator.Current.GetInstance<INewsRepository>();
//This call will be cached for 20s
var news = newsRepo.GetAllNews();

Yey! Magic! That wasn't too tricky and required zero code changes inside the repository which is neat! That's open/closed principle in SOLID principles btw. 

What if I want to clear cache for everything related to news? 

var cacheService = new Mogul.Interceptor.Cache.CacheService();
cacheService.EmptyCacheBucket("News");

Repeat step 2 and 3 for every repository you want to cache and you're done!

Advanced caching scenarios

What if I want complete control over how cache works including cache key, duration, whether to store in cache or not etc?

Use custom request and response classes like

//Response class implements ICachedResponse to make it possible to fill GotItemFromCache
public class GetNewsResponse: ICachedResponse 
{
     public bool GotItemFromCache {get;set;}
     public IEnumerable<NewsItem> Items { get; set; }
}
//Request class implements ICachedRequest to make it possible to control duration, store in cache etc in detail
public class GetNewsRequest : ICachedRequest
{
     public IEnumerable<string> CacheBuckets { get; set; }
     public TimeSpan? CacheDuration
     {
         get;set;
     }
//Set custom cache key if you want. Otherwise the cachekey will be automagically generated from request parameters and method name
     public string CacheKey
     {
         get;set;
     }
     //Might want to disable cache for authenticated users? No problem. Set this to true.
     public bool GetFromCache
     {
         get;set;
     }
     //Might want to disable cache for authenticated users? No problem. Set this to true.
     public bool StoreInCache
     {
         get;set;
     }
     public GetNewsRequest()
     {
         StoreInCache = true;
         GetFromCache = true;
         CacheDuration = new TimeSpan(0, 0, 30);
     }
}

Now you can do a call to repository like this if you want to skip cache for authenticated users (usually a good idea). 

var useCache = !User.Identity.IsAuthenticated; //Lets skip cache for authenticated users
var otherNewsResponse = newsRepo.GetNews(
                new GetNewsRequest {
                    GetFromCache = useCache,
                    StoreInCache = useCache,
                    CacheKey = "CustomCacheKey",
                    CacheBuckets =new[] { "News" }
                }
                );
//Storing in view bag to show in view for demo purpose.
//Use viewmodels... 
ViewBag.OtherNews = otherNewsResponse.Items;
ViewBag.GotFromCache = otherNewsResponse.GotItemFromCache;

You can use the other parameters in request to set cache duration, custom cache keys etc. 

The response class will contain the new bool GetItemFromCache. This is pretty useful when trouble shooting.

Logging

Logging? No problem. Just turn it on to see what's going on behind the scene

<appender name="cacheFileLogAppender" type="log4net.Appender.RollingFileAppender" >
    <!-- Consider moving the log files to a location outside the web application -->
    <file value="App_Data\Cache.log" />
    <encoding value="utf-8" />
    <staticLogFileName value="true"/>
    <datePattern value=".yyyyMMdd.'log'" />
    <rollingStyle value="Date" />
    <lockingModel type="log4net.Appender.FileAppender+MinimalLock" />
    <appendToFile value="true" />
    <layout type="log4net.Layout.PatternLayout">
      <conversionPattern value="%date [%thread] %level %logger: %message%n" />
    </layout>
</appender>
...
<logger name="Mogul" additivity="false">
    <level value="All" />
    <appender-ref ref="cacheFileLogAppender" />
</logger>

Then you will get full logs of what is going on. 

Want to rip source code and really dig deep instead? Go to my Github and feel free

Happy caching everyone!

Jan 10, 2017

Comments

K Khan
K Khan Jan 10, 2017 01:14 PM

Simply Cool!

Please login to comment.
Latest blogs
Adding Geolocation Personalisation to Optimizely CMS with Cloudflare

Enhance your Optimizely CMS personalisation by integrating Cloudflare's geolocation headers. Learn how my Cloudflare Geo-location Criteria package...

Andy Blyth | Nov 26, 2024 | Syndicated blog

Optimizely SaaS CMS + Coveo Search Page

Short on time but need a listing feature with filters, pagination, and sorting? Create a fully functional Coveo-powered search page driven by data...

Damian Smutek | Nov 21, 2024 | Syndicated blog

Optimizely SaaS CMS DAM Picker (Interim)

Simplify your Optimizely SaaS CMS workflow with the Interim DAM Picker Chrome extension. Seamlessly integrate your DAM system, streamlining asset...

Andy Blyth | Nov 21, 2024 | Syndicated blog

Optimizely CMS Roadmap

Explore Optimizely CMS's latest roadmap, packed with developer-focused updates. From SaaS speed to Visual Builder enhancements, developer tooling...

Andy Blyth | Nov 21, 2024 | Syndicated blog