State Management Part 4 - Caching Techniques

Name: *
My email: *
Recipient email: *
Message: *
Fields marked as bold are compulsory.
You haven't filled in compulsory values. The email is not correct

Most people are familiar with the concept of caching as it is widely used in computer science. Caching is a component that stores data so that we will not have to compute it all the way from the beginning next time we request it. For example, we search for data within the hard disk and when we find it we store it in the cache, as we trust it is highly possible to be requested soon, so next time we will not have to search the disk once again. Caching has a variety of uses including cpu and disk cache. This article is about the way cache can be used to in ASP.NET applications.

Getting Started

Some people may disagree as to whether caching is a state management tool or much more than that, but since it can actually be used as a state management tool I' ve decided to insert this article as part of this series.

Caching in ASP.NET can be divided into two types.

  •  Output Caching
  •  Data Caching

Output caching is used to store entire pages or controls in the cache. Data caching is used to store data in the cache. Each type can be used when it is best suited. We are going to take a good look at both of them.

Output Caching

Output caching can be used to store an entire page in the cache. The following example will show us how.

We create a page which says "Hello Chris". When you press the button it will change to "Hello Mary" and vice versa.

This is the Caching.aspx code

Hello <asp:Literal runat="server" ID="NameLitID" />

<br />

<asp:Button  runat="server" ID="ChangeNameButtonID" OnClick="ChangePerson" Text="Change Name" />

and this is the code behind part.

    private void DefaultNaming()

    {

        string name = NameLitID.Text;

        //If NameLitID says Chris, change it to Mary and vice versa

        if (name == "")

            name = "Chris";

        else if (name == "Chris")

            name = "Mary";

        else if (name == "Mary")

            name = "Chris";

       //Show member's name

        NameLitID.Text = name;

    }

Now, let's add some caching to the page. Using the

<%@ OutputCache Duration="30" VaryByParam="None" %>

in the aspx page, we cache the page for 30 seconds. The Duration parameter represents how many seconds the page will be kept in the cache. This means that "Hello Chris" will remain "Hello Chris" if we press the button before the 30 seconds timeout. However we will get "Hello Mary" after 30 seconds pass.

So far, so good. We have succeeded in caching our page. However, this way our page will always the same. Supposing that our page should say, "Hello Member_Name" to whoever may enter the site. Our code would then be useless. Let's say, we include the member ID within the query string.

Our small member catalog consists of only two members, Chris and Mary that have member IDs equal to 1 and 2. Which member requests the page should consist of the MemberID parameter of the QueryString. We are going to take a thorough look on how the query string works next time. For now, let's go on with our example.

We use the following new piece of code in the code behind page.

     private void NamingWithQueryStringCaching()

    {

        //Gets memberID from Query String

        string memberID = Request.QueryString["MemberID"];

        string name = "Unknown";

        // Chris's member ID = 1 and Mary's member ID = 2

        if (memberID == "1")

            name = "Chris";

        else if(memberID == "2")

            name = "Mary";

        //Show member's name

        NameLitID.Text = name;

    }

We also need this method to be called when the button is pressed in order to switch members.

    private void ChangePersonWithQueryStringCaching()

    {

        //Gets memberID from Query String

        stringmemberID = Request.QueryString["MemberID"];

        //Redirect to the other member's page.

        if ((memberID == null) || (memberID == "1"))

            Response.Redirect("Caching.aspx?MemberID=2");

        else if (memberID == "2")

            Response.Redirect("Caching.aspx?MemberID=1");

    }

In that case we would have to use the VaryByParam parameter. This is the name of the query string's parameter for which we wish to store a different copy in the cache for each different value. We will set our VaryByParam to MemberID. That way MemberID 1, will have its own copy in the cache, separate from the one MemberID 2 will.

So,<%@ OutputCache Duration="30" VaryByParam="MemberID" %> it is.

In this example, the first time we request the page we get "Hello Unknown". When we press the button we get "Hello Mary" and this instance is stored in cache combined to MemberID 2. Next time we press the button we get "Hello Chris" and the new instance is stored in cache combined to MemberID 1. If we keep pressing the button all results we get, will come straight from the cache.

Output Caching attributes

Apart from what we already examined, Output Caching has a few more handy attributes.

varybycontrol can be used to store another copy of the page if its controls have different output.

Using varybycustom we can create a method which will decide whether the output should use the cache or create a new instance.

varybyheader can be used to create different copies depending on the given header contained in the request. For example 

<%@ OutputCache Duration="30" VaryByParam="None" VaryByHeader="Accept-Language"%>

can be used to produce different copies based on the browser's language.

All principles we have talked about so far can also be used in user controls contained within a page. So, if we wish we can cache only a single element (control) within a page and create the rest from the beginning. This is called Fragment Caching.

Data Caching

Instead of caching a full page or control why not cache only some data within it which is hard to get? This is the essence of data caching. It is most often used to store data obtained from the database so, we will not have to hit it over and over again.

There are three ways to store a value in the cache. The first is to use the cache as you would a dictionary.

Cache["key"] = value;

For example

private void DataCaching()

    {

        string message;

        //Always check for null reference

        if (Cache["message"] != null)

            //Gets value from Cache

            message = Cache["message"].ToString();

        else

            //Sets value to Cache

            Cache["message"] = "Hello World";

    }

However this way offers no control over the time the value will be stored to the cache or other attributes so it is not much used.

Removing items from the Cache

Before we go on and find out the two other ways to insert data to cache it would be a good idea to take a look at the ways data can be removed from the cache. Data stored using output caching or the previous way of data caching will not be removed until the cache is filled with data. In that case ASP.NET will automatically remove data which haven't been used for some time base on the LRU algorithm. This technique is called scavenging. The cache size can be configured in the machine.config file.

However using Cache.Add or Cache.Insert we are given more choices of handling the cache. The first one is time expiration. When the specified time has passed, the item will be removed. Second are item dependencies. When one of the dependencies is changed the data is removed. You can also use priorities, in order to make ASP.NET decide which items to remove in case of scavenging when more than items are about to be removed.

 We can also remove an object from the cache ourselves. All we have to do is use the method Cache.Remove(key).

 For example :

 Cache["message"] = "Hello World";

 Cache.Remove("message");

 To remove an item from the cache we do not have to check for null reference. If the item does not exist, nothing happens

 

Cache.Insert and Cache.Add

Cache.Insert is probably the method most developers use to insert data into the cache. Cache.Insert has many overloads. The utlimate insert has the following form:

Cache.Insert(key, value, dependencies, absoluteExpiration, slidingExpiration, priority, onRemoveCallback)

Key and value parameters we’ve already covered. Let's take a look at the rest.

In order to create a dependency we first have to create a CacheDependency object. That object will be pointed to a referenced resource, for example a file or folder. When this resource is modified, the item will be removed from the cache.

There are two kinds of expirations. Absolute and sliding. When using absolute e.g. DateTime.Now.AddMinutes(60), the item will be removed from the cache one hour from now, no matter what. If you had used sliding expiration instead, the countdown timer would be reset when we got a new request. Thus, using sliding expiration, we can keep an item stored in the cache much longer than the value we would at first point.

Priorities we have covered as well. The higher the priority, the less possible the item to be removed from the cache. Worth mentioning is the value NotRemovable which will prevent scavenging from removing this item.

onRemoveCallback parameter can be a method that will automatically be invoked when the item is removed from the cache.

An example of Cache.Insert would be            

Cache.Insert("message", "Hello World", null, Cache.NoAbsoluteExpiration, TimeSpan.FromSeconds(30), CacheItemPriority.High, null);

Cache.Add works about the same way as Cache.Insert, however if a value with the same key has already been inserted within the cache, then that value would not be replaced. As most applications would ask for latest values to be stored, Cache.Add is not much used.

The following example will show an actual use of caching when we want to show a list of news objects in our website's news page.

The method will first check if our list exists within the cache. If it does, it will copy it straight from the cache. If not, it will get the data from the database and form them into a list, storing it into the cache before returning it.

public static List<New> GetNewsList()

        {

            if (HttpRuntime.Cache["NewsList"] == null)

            {

                List<New> newsList = new List<New>();

                  //Code that fills newsList from the database should be used here

                  //Then insert it to the cache

                HttpRuntime.Cache.Insert("NewsList", newsList, null, DateTime.Now.AddMinutes(Convert.ToInt16(ConfigurationManager.AppSettings["CacheDuration"])), TimeSpan.Zero);

                return newsList;

            }

            else

                return (List<New>)HttpRuntime.Cache["NewsList"];

        }

 When should I use caching?

 Caching can be used to store everything. However using cache to store data that have to do with clients wouldn' t be a good idea as if, for example, 100 users visit your site, this will end up having 100 different cache items using names like User1Data, User2Data etc. Session and cookies are much more usefull in that case. Cache’s true power lies in storing data that are extracted from the database and can be used by everybody. Proper use of cache can minimize the response time of web page. Being a server tool cache is completely safe from attacks.

 

 Conclusion

 Cache is a very handy tool to store data that it would take much time to calculate. There are two kinds of caching in ASP.NET output and data caching. Output caching can store entire pages and controls while data caching stores single part of information. Data caching offers many ways to handle your cache like dependencies, priorities and timeouts. Mastering caching can be a key to creating a much faster website.

Back to BlogPreviousNext

Comments



    Leave a comment
    Name: