Which is one of the most annoying things in a web site? Most of the users will answer – to be slow. Personally, for me to try to reach information from some page which seems that will take forever to load if really frustrating. But how can we as developers make it faster? Good quality code, you will say. Yes, but since even the most well written and structured code could provide a big difference in performance, many times the answer is just caching. In the general case users don’t need live information. A couple of minutes delay are not something that they will notice, but a couple of minutes loading a page? I will leave this site as soon as possible and do my best to never comeback to it.
I’m a web developer and I use SDL Tridion for parts of my work. In the new way the system is going – DD4T – everything is dynamic. We just get the content on demand, it is great, and it is flexible… but it can be soooo slow. So if your site is getting a lot of information dynamically you should definitely consider caching.
.Net provide us with two main types of caching mechanisms – output and data caching.
A very good option is the “Output cache” provided by ASP.NET. To use it in MVC we just put the “OutputCache” attribute with a “Duration” property above an action or a controller. With this we cache the result of the method, or of all methods in the specified controller.The properties for configuring the OutputCache are:
Duration - this property is mandatory. It specifies the time for which the result form this action will stay in the cache.
Usually we don’t make just static pages so we need to make more adjustments for the output cache and to specify more of its properties:
VaryByParam – sets one or more parameters by which the cache varies. If we have a several parameters they are separated with semicolon. For every value that they take a new instance of the result returned by the action is created in the cache. In some older .NET versions this attribute is mandatory, but in .NET 4.5 if we don’t specify a value it will be equivalent to VaryByParam=”*”, meaning vary for every parameter possible. Another option you can put as value is “none” – meaning there will be only one instance of this page in the cache, no matter if any of the parameters change.* In this example we have a search string based on whom we return a different result. In this particular scenario the caching is probably not a good idea, because if you have hundreds of users that make hundreds of searches with a different search string there will be a lot if instances of this page in the cache.
Another example – we have a few colors that the user can choose and something different happens for every one of them – this is a good candidate for caching. There will be just a several different instances in the cache.
VaryByHeader – accepts a name of an http header(s). For every value that the header takes a new instance of the result returned by the action is created in the cache.* In this example we have resource files and based on the language header we use them to translate the buttons, the labels and the other content in our page. For every language different instance will be created in the cache.
VaryByCustom – accepts a list of custom strings that the output cache uses to vary the cache entry. If a custom string is expected, we should override the HttpApplication.GetVaryByCustomString method in our application Global.asax file.* In this example we specify a “browser”. The cache entry will vary by the browser type and the major version number. So if we have a different content for the different browsers this is the way to use the cache and still have a correct content for every browser.
Location – This attribute determines the location for the cached item. We can specify one of the following locations:
- Any – stores the output cache on the client’s browser, on the proxy server (or any other server) that participates in the request, or on the server where the request is processed. By default, this is the selected value for the location attribute.
- Client – stores the output cache on the client’s browser.
- Downstream – stores the output cache on any cache-capable devices (other than the origin server) that participates in the request.
- Server – stores the output cache on the Web server.
- None – turns off the output cache.
SqlDependency – with this property we can set a dependency between the database and the cache. At any time something is changed within the table(s) we are tracking, the new instance of the action result will be created in the cache.
This can be implemented by the following steps:
- Configure the sql dependency ‘profile’ in the Web.config.
- Enable notifications in the Global.asax for the database and for the table(s) that you want to track for changes.* In this case the notifications will be enabled for the “Languages” table.
- Set the name of the SqlDependency ‘profile’ and the name of the tracked table as a value of the attribute.
CacheProfile – our main goal using the cache is to improve the performance of our site. But to do this we have to deploy the application in the web, and try how it works, measuring the performance by changing the cache settings. So to avoid the republishing of the application at any time we change some property we can make an Output cache profile in the Web.config. Using this we also avoid the code repetition. So if we have a several actions that we want to cache for the same time, at the same location and vary them by the same headers we can use one profile for all of them. If one of the actions needs to be vary by parameters we are able to use the ‘VaryByParam’ and get the other properties from the web.config profile.
This will be the configuration in the web config:And in the controller:
In some scenarios we need to cache only the data and not the whole page. Then we can use “HttpContext.Cache”. There is only one instance of the Cache class per application domain. As a result, the Cache object that is returned is the same Cache object used in all requests in the application domain.
Working with this cache is a lot easier, there is only one instance of the cached item. We must choose a key with which we will save and then reach our data.
This are the properties for configuring our data cache:
Key – the name with which will access our item.
Value – the object that we want to store in the cache.
Dependencies – used to configure our cache dependencies, indicating when our object will be removed from the cache.
The data cache provides an option to listen for caches in a file, so if we are reading our data from a file(s) this is very useful. The dependency must be an instance of the CacheDependency class. When we create the instance we give the absolute path of the file. For tracking multiple files we just give an array of their paths.
Other option is tracking cached item(s). If any of the values responds to any of changed objects, the cache dependency will be triggered and our cached item will be removed from the cache.
We can, also, add a different dependencies as a parameter to our dependency.* In this example we are caching a list of users and configuring the ‘firstDependecy’ to listen for changes in the ‘Text.txt’ file and in the ‘SecondKey’ cached item. The ‘SecondKey’ key is added to the cache and we set it to listen for changes in the ‘Text2.txt’ file. So if something is changed in the ‘Text2.txt’ this will be heard from our first dependency and the list of users will be removed from the cache.
AbsoluteExpiration – configures the time for which our object will stay in the cache.
SlidingExpiration – sets how much time after the last accessing our cached object will expire. To be able to use this one we should configure the AbsoluteExpiration property to Cache.NoAbsoluteExpiration.
* In this example the object under the key ‘Users’ will expire if nobody access it for 5 minutes.
CacheItemPriority – defines the priority of the cached item – the items with a lower priority will be removed first when the server releases the system memory. The default value is normal.
CacheItemRemovedCallback – defines what happens when a data in the cache expires. This property needs an instance of the CacheItemRemovedCallback class. The method that we pass to the CacheItemRemovedCallback constructor should be ‘void’ with the following parameters – string key, Object val, CacheItemRemovedReason reason.
In the article we’ve looked over two mechanisms of using cache with asp.net applications
– Output cache – for the whole controller or a method, with many possible instances.
– Data cache – for objects that are time consuming to be reached or calculated.
The cache is configurable and flexible and can be modified in the way that the best suits your needs. Sometimes the cache is the only way to improve the performance of your web application, but in many times it’s overlooked or used in the wrong way. I want to make a final remark to the reader – use cache only where it is needed. Don’t just cache everything. Consider the real life situations in which the user will access your pages and the way he will access them. Do you have many users reaching one page once, or not so many but every one of them is making many requests to the same resource? Depending on the situation the cache should be on the server, or on the client side, or on both. Do we have countless varieties of a possible parameter values, if yes, it’s not good idea your cache to vary by this parameter. There are many sides of the caching, and it should be well understood before it’s used. In this article I’m trying to introduce to the reader with the technical methods the framework provides us, but the way they should be used is depending entirely on your application structure and the manner it will be used.
Have a nice time caching!