The EPiServer API does a lot of caching in the background to help improve your website's performance. This is one of the main reasons Episerver API works so quickly. It also makes the developers' lives easier as it is normally one less thing to worry about. When we build large scale applications that get large numbers of traffic, it is important to understand how and when EPiServer caches data to ensure your website runs in the most efficient manner.
The object cache is built on top of the ASP.NET runtime cache. As mentioned in Why are Pages and Blocks Returned From The Episerver API Read-Only?.
The API only returns read-only objects for better performance. This mechanism enhances scalability, as multiple threads can reading the same data.
The Get<>() method takes a ContentReference and returns the related object. On the first request, the API calls the database (as the cache is empty) and creates an in-memory representation of the requested object. After the object has been created it will be placed in the page cache using a unique cache key. From that point forward, until the cache expires, that object will be served from memory
Storing the results the list from cache improves performance by reducing the number of database requests. There are several edge cases where the API will not cache your objects:
- If the WorkId in the Content References is set to a specific version, the cache will be bypassed. As long as you want to access the latest version the API will cache the object. If you want to work with a specific version of the page (by setting the WorkId) the API will make a call to the database.
Working with older versions of your pages and block happens very rarely so it works out quite well. Personally, whenever I have needed to access older versions of a page in code, for example, it has always been in some type of back-end admin plug-in, so performance isn't as big a deal in those scenarios.
- When you are viewing a page in the editor it will not be cached. This happens because when Episerver serves a page in 'edit mode' it does not use a read-only version. Instead, it uses a writable version, to allow content editors to update a page/block via the editor.
When an item is cached, it will be set to a default expiration period of 12 hours. This value is the out of the box setting, but you are free to set this to any value you want. If you want to know more about this, look at .
To change the timeout period, add an entry called pageCacheSlidingExpiration in your 'applicationSettings' within the Episerver section (You will find this in either the web.config or the episerver.config). To set the cache timeout to an hour, your setting would look like this:
Before we get into the GetChildren<>() method, it is probably a good time to explain what Episerver caches. EPiServer will cache the current version for all content (after an initial call for that object is made), it does not, however, cache the page tree hierarchy/content structure directly.
The page tree is never stored in one large collection behind the scenes, even if all the pages are stored in the cache. What it does do, however, is cache the results of GetChildren() in individual collections. When GetChilden<>() is called, it will look in the cache for a collection of ContentReferences (note references and not the objects themselves) that would represent all the child pages.
If the cache is empty, it will go and populate the list from the database and store it in the cache. The API will then fetch each item from the list of content references and add them to a PageDataCollection . GetChildren<>() will then return this collection to the caller. The API retrieves the page objects using the Get<>() method. It uses the same cache key as Get<>(), checks if the key is in the cache already, otherwise it goes off to the database to retrieve it and then add it to the cache.
If a content editor updates a page or a block, for instance, as long as the parent does not change, the collection of contents references stored by GetChildre<>() does not need to be updated and can still live in cache unmodified. Only the item that got updated needs to be refreshed.
FindPagesWithCriteria() and FindAllPagesWithCriteria()
I've talked previously about FindPagesWithCriteria, in FindPagesWithCriteria: How to search for pages within Episerver but I will quickly cover how FindPagesWithCriteria uses the cache in the back-end.
The mechanics of FindPagesWithCriteria is kind of similar to GetChildre<>(). It performs a query to the database to get a collection of content references for the matching results of the search. It then goes off and fetches each page individual object in the list using Get<>() to get the cached results. The difference between GetChildren<>() and FindPagesWithCriteria() is that the collection of ContentReferences created in FindPagesWithCriteria is not cached and needs to be generated each time the query is run.
If you think about it, this makes sense. You would normally only consider using FindPagesWithCriteria when you need to query items that may live anywhere in the page tree, for example, like a site search. In these instances, a search might change regularly, so caching the content collection list doesn't make a lot of sense.
My recommendation is to always favour GetChildren() as it uses the cache. FindPagesWithCriteria() is more performance-intensive but if you need to query a lot of things from the whole of the page tree then it is quicker than constantly having to iterate over everything using GetChildren().
Another handy tip when designing your site is that you should never try to cache the content objects yourself in memory. Attempting to cache PageData objects yourself will only result in complications like worrying how you to invalidate the cache when content editors update content.
You should focus more on caching the content reference or ID's instead. As the Get<>() method takes care of caching the page and block objects for you, making a call to using Get<>() is pretty efficient.