Caching items in Orchard

(c) Bertrand Le Roy 2010Orchard has its own caching API that while built on top of ASP.NET's caching feature adds a couple of interesting twists.

In addition to its usual work, the Orchard cache API must transparently separate the cache entries by tenant but beyond that, it does offer a more modern API.

Here's for example how I'm using the API in the new version of my Favicon module:

_cacheManager.Get(
    "Vandelay.Favicon.Url",
    ctx => {
        ctx.Monitor(_signals.When("Vandelay.Favicon.Changed"));
        var faviconSettings = ...;
        return faviconSettings.FaviconUrl;
    });

There is no need for any code to test for the existence of the cache entry or to later fill that entry. Seriously, how many times have you written code like this:

var faviconUrl = (string)cache["Vandelay.Favicon.Url"];
if (faviconUrl == null) {
    faviconUrl = ...;
    cache.Add("Vandelay.Favicon.Url", faviconUrl, ...);
}

Orchard's cache API takes that control flow and internalizes it into the API so that you never have to write it again. Notice how even casting the object from the cache is no longer necessary as the type can be inferred from the return type of the Lambda.

The Lambda itself is of course only hit when the cache entry is not found. In addition to fetching the object we're looking for, it also sets up the dependencies to monitor.

You can monitor anything that implements IVolatileToken. Here, we are monitoring a specific signal ("Vandelay.Favicon.Changed") that can be triggered by other parts of the application like so:

_signals.Trigger("Vandelay.Favicon.Changed");

In other words, you don't explicitly expire the cache entry. Instead, something happens that triggers the expiration. Other implementations of IVolatileToken include absolute expiration or monitoring of the files under a virtual path, but you can also come up with your own.

8 Comments

  • I'm not familiar with the triggering. Does this then invalidate the cache on the local machine only? What if you're using this on a web farm?

  • Very cool! So when you signal a trigger, when does that item actually get removed from cache? is there a background process that runs ever so often?

  • @Marco: I think all listeners get the signal immediately and can expire the cache entry right away.

    @Nairb: that is a very good remark, and for the moment we are not supporting that scenario. I filed this bug to track the issue: http://orchard.codeplex.com/workitem/17361

  • Nice article! But can we get the item from cache without specifying lambda as a parameter? I have a couple of places where the cache item gets retrieved (eg. different actions of the same controller), but only in one of those I want to define the lambda. It would be great if there would be an override which would just throw exception/return null if the cache item is not found.

  • @Piotr: you don't want to do that. Actually you can throw or return null from the Lambda, but the Lambda is still mandatory. There are all kinds of subtle things going on here that could be the subject of another post.

  • @Bertrand. I like it a lot. Similar to what I've built.

    @Piotr: No, you should NEVER have that sort of login in your controllers... put that logic in a repository class and call the repository from your "several places" in the controller.

  • @Marc: Yes, I know:) - I just thrown an unfortunate example... Thanks for noticing that.

    @Bertrand: But when I specify a null lambda for an item, which had it specified before, isn't it going to be overwritten? Basically, what I'd want to achieve is a "get" behaviour on a cache item, not a "get/set" one. I would like to check if an item exists in cache (and get it if it is). As for now the Get method always updates the cache dictionary...

    Scenario: I have a complex object fetched from a WCF service (which is costly) and cached at point 1. This item gets invalidated at point 2 (by IClock or ISignal), but I don't want to renew it instantly - only when user once again reaches point 1. I have to be aware of the moment when the cache gets invalidated to display an appropriate info.

  • @Piotr: that is a somewhat unusual scenario. What do you tell the user if you don't find the object in the cache?
    You could cache a token instead of refectching. When getting from the cache from point 1 and finding the token, you'd know to re-fetch.

Comments have been disabled for this content.