What is Caching
Caching is a technique of persisting the data in memory for immediate access to requesting program calls. Many in the developer community consider caching as one of the features available to improve performance of Web applications.
Consider a page that has list of Employee name, contact numbers and mail-Ids of existing employees of a company on an intranet accessible by all employees. This is very useful information that is available throughout the company and could also be one of the most accessed pages. The functionality of adding, updating or deleting is usually less intensive compared to more transaction-based systems like Purchase ordering, Voucher creation etc. Now in a normal scenario the
process of querying database for each request is not cost-effective in terms of server resources, hence is lot better to cache or persist the data to avoid this costly loss of resources.
Web caching technology in ASP.NET and C# is helpful for popular website reducing its server workload and improving access times. This tutorial will show you how to use web caching save data to RAM, and improve data access times therefore.
First, import the namespace of System.Web.Caching
Declare the variables
static bool itemRemoved = false;
static CacheItemRemovedReason reason;
CacheItemRemovedCallback onRemove = null;
Define the method of AddItemToCache, it will use Cache.Add to add items to cache
public void AddItemToCache(Object sender, EventArgs e)
itemRemoved = false;
onRemove = new CacheItemRemovedCallback(this.RemovedCallback);
if (Cache["Key1"] == null)
Cache.Add("Key1", "Caching", null, DateTime.Now.AddSeconds(60), TimeSpan.Zero, CacheItemPriority.High, onRemove);
Define the method of RemoveItemFromCache, it will use Cache.Remove to remove items from cache
public void RemoveItemFromCache(Object sender, EventArgs e)
if (Cache["Key1"] != null)
When using the method of Cache.Remove , it will be leaded to invoke RemovedCallback method
public void RemovedCallback(String k, Object v, CacheItemRemovedReason r)
itemRemoved = true;
reason = r;
protected void Page_Load(object sender, EventArgs e)
Response.Write("RemovedCallback event raised.");
Response.Write("Reason: " + reason.ToString() + "");
Response.Write("Value of cache key: " + Server.HtmlEncode(Cache["Key1"] as string) + "");
The HTML of the web page
The .NET Advantage
ASP.NET provides the flexibility in terms of caching at different levels.
Page Level Output Caching
This is at the page level and one of the easiest means for caching pages. This requires one to specify Duration of cache and Attribute of caching.
The above syntax specifies that the page be cached for duration of 60 seconds and the value "none" for VaryByParam* attribute makes sure that there is a single cached page available for this duration specified.
VaryByParam can take various "key" parameter names in query string. Also there are other attributes like VaryByHeader, VaryByCustom etc. Please refer to MSDN for more on this.
Even though this definition refers to caching portion/s of page, it is actually caching a user control that can be used in a base web form page. In theory, if you have used include files in the traditional ASP model then this caching model is like caching these include files separately. In ASP.NET more often this is done through User Controls. Initially even though one feels a bit misleading, this is a significant technique that can be used especially when implementing "n" instances of the controls in various *.aspx pages. We can use the same syntax that we declared for the page level caching as shown above, but the power of fragment caching comes from the attribute "VaryByControl". Using this attribute one can cache a user control based on the properties exposed.
The above syntax when declared within an *.ascx file ensures that the control is cached for 60 seconds and the number of representations of cached control is dependant on the property "DepartmentId" declared in the control.
Add the following into an *.ascx file. Please note the use of tag "Control" and the cache declaration.
Add the following to an *.aspx file. Please note the way "Register" tag is used; the declaration of control using syntax <[TagPrefix]:[TagName]>; Usage of property " DepartMentId". Open the page in two browsers and closely watch the Base form timing and the User control timing. Also note that the following page results in two copies or representation of user control in the cache.
Application Level Caching
With Page level Output caching one cannot cache objects between pages within an application. Fragment caching is great in that sense but has limitations by using user controls as means to do. We can use the Cache object programmatically to take advantage of caching objects and share the same between pages. Further the availability of different overloaded methods gives a greater flexibility for our Cache policy like Timespan, Absolute expiration etc. But one of the biggest takes is the CacheDependancy. This means that one can create a cache and associate with it a dependency that is either another cache key or a file.
In almost all Web applications there could be numerous master tables that act as lookups to application specific tables. For e.g. if you take up adding a Employee, usually one has master tables like "tblQualification" to get list of qualifications, "tblLocations" to get list of locations etc. These tables* are usually set during the initial application configuration phase and could be modified once a month or even less than that. Hence it makes sense for us to use them in our Cache rather than making calls to database on each request. But then what Cache Policy do we adopt?
We cannot hold these objects in Cache for entire application instance, because if anybody changes data in these tables one has to also refresh the cache. It is here that CacheDependancy can be used.
Even though these tables are less frequently used for updates, they are extensively used in our select statements through out the applications.
Find below the snippet that uses CacheDependancy. Here what I have done is to provide a list view of existing employees. You need to create a Database in Sql Server, setup some data before you can continue.
Add database connection value in Web.Config and change the value as per your setup.
First I get the dataset into which I fill the user list. But before this I check for the cache initially if it exists I directly cast it to a dataset, if not create a cache again.
I create the cache with "Users" as key using Cache.Insert* and link this with a file "Master.xml". This "Master.xml" is a XML file that contains Master data of "tblQualifications" and "tbllocations". I have used "Server.MapPath" to get the physical path of the file on the server. The CacheDependancy instance will make sure that any change in this dependency file means that you need to recreate your cache key definition. This is a great feature to use since I can recreate my cache only when required instead of caching the data at the page level.
For other overloaded parameters refer MSDN.
Also note how we could use trace within to add my own statements.
We created the page that initiates and uses the Cache. For testing purpose we need another page that will overwrite this "Master.xml" on click of a button for which the code snippet is as follows. This ideally should be our master maintenance page that adds/updates Master records in database and overwrites the XML. But to make it easy I have just written an overwriting sample.
The following C# code snippet is a technique to get the best of both worlds. I personally use this all the time in my development.
public ArrayList GetUsers(bool AllowCache)
// this directive will force the variable AllowCache to
// false while in 'debug mode' in Visual Studios .NET
AllowCache = false;
string strCacheKey = "Users-GetUsers";
if( AllowCache && ( null != HttpContext.Current.Cache[strCacheKey]) )
// code to fetch users from database
ArrayList alUsers = null;
alUsers = Users.GetUsers();
// cache it if 'allowed'
if ((AllowCache) && (HttpContext.Current.Cache[strCacheKey] == null))
HttpContext.Current.Cache.Insert(strCacheKey, alUsers, null, DateTime.Now.AddMinutes(5), TimeSpan.Zero);
So as you can see, the method has a parameter of type bool (true/false) that gets passed in while you’re calling the method. If you want to get live data, you pass in FALSE, for cached data you pass in TRUE. As an added bonus, you can add the #if DEBUG directive to your code to allow for non-cached data to be viewed while your coding your application in debug mode. If you switch to ‘release’ in VS.NET you will get cached data (assuming you passed in TRUE).
Now once you have created the above pages i.e. one that implements caching and other that overwrites the dependency file, create two instance of browser and open the cache implementation page and note for trace, label text; open the other instance of browser with the page which overwrites the XML. Note the former, the first time it fetches data from the database and the subsequent request will be from cache till your expiration time of 45 seconds is reached or anyone overwrites or changes the "Master.xml" file. Also give a look on Timespan parameter since you have a concept of Sliding expiration that can also be implemented. Keep refreshing the first page and you will see that trace indicates the cached page retrieval. Click the overwrite XML button on the latter page that would overwrite the XML and again refresh the former page to note that the data is retrieved from database. Though in this example I have not shown any direct relation between the cached data and the dependency file (like get values from dependency file and merge with cached object etc) in terms of integrated usage, this could very easily be designed and implemented. Dependency caching is a powerful technique that .NET supports and should be utilized wherever applicable.
Usama Wahab Khan and Atif Shahzad
Sunday, August 2, 2009
What is Caching
Posted by Usama Wahab Khan at 9:32 PM