Caching Application Data by Using Multiple Cache Objects in an ASP.NET Application

I am in the process of writing a Walkthrough document about How to use multiple cache objects to cache application data in ASP.Net 4. This blog post is intended to be a precursor for the Walkthrough. When the Walkthrough is completed I intend to submit it to MSDN for publication as part of the ASP.NET documentation set. Please provide your feedback to help improve this upcoming document.

Introduction

ASP.NET lets you use multiple cache instances in an ASP.NET Web site application—that is, you can instantiate different cache classes and store data in the different instances. You can use different cache instances to cache the same data, or you can use different cache instances to cache different data. For example, for one cache instance, you might let the cache use as much memory as possible, but for the other implementation, you might specify a maximum amount of memory that the cache can use. Using multiple cache objects lets you separate different kinds of data into different caches and enables you to use different caching strategies for different kinds of data.

This walkthrough shows you how to use different instances of the memory cache object in the same ASP.NET application in order to cache the contents of different text files. You will use the built-in in-memory cache object to cache the contents of one text file. For this cache, you will allow the cache to expire every 10 seconds or when the file is changed.

For the second cache instance, you will use a custom cache class that inherits from the memory class in order to cache the contents of a second text file. The custom class demonstrates how you would store the cached data in a named region in memory. The built-in memory cache object does not support the concept of regions but it is used in this blog to demonstrate how you would implement regions in a custom cache class. A cache region is a partition in memory that is reserved for storing one or more cache objects. A cache region helps organize cache objects in memory.

Note: Although the built-in memory cache object does not support regions, the regions partition concept is used with the built-in memory cache class in this blog for demonstration only.

Tasks illustrated in this walkthrough include the following:

· Creating an ASP.NET Web site.

· Adding a reference to the .NET Framework.

· Adding a cache class that caches the contents of a text file by using the built-in memory cache object.

· Adding a cache implementation that caches the contents of a text file by using a custom cache object.

· Adding data to each cache.

· Testing caching.

Prerequisites

In order to complete this walkthrough, you will need:

· Microsoft Visual Studio 2010.

· Two text files, each containing a small amount of text. You will display the contents of the text files in a Web page.

Creating an ASP.NET Web Site

You will start by creating an ASP.NET Web site.

To create an ASP.NET Web site

1. Start Visual Studio 2010 or Visual Web Developer Express 2010.

2. In the File menu, click New Web Site. (If you do not see this option, click New, and then click Web Site.)

The New Web Site dialog box is displayed.

3. Under Installed Templates, click Visual Basic or C# and then select ASP.NET Web Site.

4. In the Web Location box, select File System and enter the name of the folder where you want to keep the pages of the Web site. For example, enter the folder name C:\Websites\MultipleCaching and then click OK.

Visual Studio creates the folder and opens the Web site in Source view. Notice that the root Web site contains several files and folders including the Account folder, a Web.config file, the About.aspx and Default.aspx pages, and the Site.master master page.

The next step is to add the text files you want to use to the current Web site project.

To add the text file to the project

1. In Solution Explorer, right-click the name of the project and then click Add Existing Item.

2. In the Add Existing Item dialog box, select the text file that you want to use for this walkthrough, and then click Add to add one text file.

3. Repeat the steps to add the second text file

Adding a Reference to the Caching Assemblies

To use the caching classes that are defined in the System.Runtime.Caching namespace in an ASP.NET application, you must add a reference to the namespace.

To add a reference the Website

4. In Solution Explorer, right-click the name of the Web site and then click Add Reference.

5. Select the .NET tab, select System.Runtime.Caching, and then click OK.

Adding Controls to the ASP.NET Page

To illustrate how to use caching, you will add two Button controls and a Label control to the page. You will create an event handler for each button's Click event. Later you will add code to the event handlers that reads the cached data and displays it in the Label control.

 

To add controls to the page

6. From the Standard tab of the Toolbox, drag two Button controls to the Default.aspx page.

7. In the Properties window, set the Text property of one of the Button controls to Get from Built-In Cache. Accept the default ID property.

8. Set the Text property of the other Button control to Get from Custom Cache. Accept the default ID property.

9. From the Standard tab of the Toolbox, drag a Label control to the page. Accept the default ID property.

 

Initializing the Built-in Cache and Caching Data

Next, you will add code to create an instance of the built-in cache object. The built-in cache object caches data in memory.

The code will perform the following tasks:

· Create an instance of the System.Runtime.Caching.MemoryCache class.

· Specify that the cache uses a System.Runtime.Caching.HostFileChangeMonitor object to monitor changes in one of the text files that you are using in this walkthrough

· Read the text file and cache its contents as a cache entry.

· Display the contents of the cached text file.

 

To create an instance of the built-in memory cache object

10. Double-click the Get from Built-In Cache button to create an event handler in the Default.aspx.cs or Default.aspx.vb file.

11. At the top of the file (before the class declaration), add the following using (C#) or Imports (Visual Basic) statements.

 

[Visual Basic]

Imports System.Runtime.Caching

Imports System.IO

 

[C#]

using System.Runtime.Caching;

using System.IO;

12. In the event handler, add the following code to instantiate the cache.

[Visual Basic]

Dim cache As ObjectCache = MemoryCache.Default

 

[C#]

ObjectCache cache = MemoryCache.Default;

The System.Runtime.Caching.ObjectCache class provides an in-memory object cache.

Note   The System.Runtime.Caching.ObjectCache class is a replacement for the System.Web.Caching.Cache class. In ASP.NET 4, caching is implemented by using the System.Runtime.Caching.ObjectCache class.

13. Add the following code to read the contents of a cache entry named filecontents.

[Visual Basic]

Dim fileContents As String = TryCast(cache("filecontents"), String)

 

[C#]

string fileContents = cache["filecontents"] as string;

14. Add code to perform the following tasks:

a. Check whether the cache entry named filecontents exists. If the specified cache entry does not exist, you must read the text file and add it as a cache entry to the cache.

b. Create a new System.Runtime.Caching.CacheItemPolicy object that specifies that the cache expires after 10 seconds.

c. Create a collection for the file paths you want to monitor and to add the path of the text file to the collection.

d. Add a new System.Runtime.Caching.HostFileChangeMonitor object to the collection of change monitors for the cache entry. The System.Runtime.Caching.HostFileChangeMonitor object monitors the text file's path and notifies the cache if changes occur. In this example, the cache entry will automatically expire if the contents of the file changes.

e. Read the contents of the text file.

f. Insert the contents of the file into the cache object as a System.Runtime.Caching.CacheItem instance. You specify information about how the cache entry should be evicted by passing the System.Runtime.Caching.CacheItemPolicy object as a parameter Set method.

The following example shows code that accomplishes these tasks.

[Visual Basic]

If fileContents Is Nothing Then

    Dim policy As New CacheItemPolicy()

    policy.AbsoluteExpiration = _

    DateTimeOffset.Now.AddSeconds(10.0)

    Dim filePaths As New List(Of String)()

    Dim cachedFilePath As String = Server.MapPath("~") & _

                "\cacheText.txt"

    filePaths.Add(cachedFilePath)

    policy.ChangeMonitors.Add(New _

        HostFileChangeMonitor(filePaths))

    ' Fetch the file contents.

    fileContents = File.ReadAllText(cachedFilePath) & _

            vbCrLf & " Using built-in cache " & _

 

        DateTime.Now.ToString()

     cache.Set("filecontents", fileContents, policy)

End If

 

[C#]

if (fileContents == null)

{

    CacheItemPolicy policy = new CacheItemPolicy();

            policy.AbsoluteExpiration =

            DateTimeOffset.Now.AddSeconds(10.0);

            List<string> filePaths = new List<string>();

        string cachedFilePath = Server.MapPath("~") + "\\cachedText.txt";

            filePaths.Add (cachedFilePath);

            policy.ChangeMonitors.Add(new

            HostFileChangeMonitor(filePaths));

            // Fetch the file contents.

          fileContents =

            File.ReadAllText(cachedFilePath) + "\n" +

                " Using built-in cache " + "\n" + DateTime.Now.ToString();

               

            cache.Set("filecontents", fileContents, policy);

}

15. Add the following code to display the cached file content in a Label control.

[Visual Basic]

Label1.Text = fileContents

 

[C#]

Label1.Text = fileContents;

 

Creating the Custom Cache Class

Next, you will create a custom cache class named CustomCache. In this example, the custom class will inherit from the build-in System.Runtime.Caching.MemoryCache class. You will override the methods to provide a custom implementation. You can use the API in the System.Runtime.Caching namespace to implement region partitions in a cache class that supports regions. For example, if your custom class inherits from a cache that supports regions, such as the Windows AppFabric caching (formerly code-named Microsoft Velocity), your custom class will support creating cache objects with region partitions. The built-in memory cache object does not support the concept of regions partitions but it is used in this blog to demonstrate how you would implement region partitions in a custom cache class.

Overloads of the set methods provide different ways to add a cache item to the cache. A cache item can be added by specifying the cache item, an expiration policy, a key/value pair named region in memory where the cache should be stored and so on.

 

To create the custom cache class

16. In Solution Explorer, right-click the name of the Web site and then click Add New Item.

The New Item dialog box is displayed.

17. Under Installed Templates, select Visual Basic or Visual C# and the select Class.

18. In the Name text box, enter the name CustomCache and then click Add.

19. In the dialog box that appears, click Yes to put the class in the App_Code folder

20. In the code editor, replace the existing contents of the class file with the following code:

[Visual Basic]

Imports System

Imports System.Web

Imports System.Runtime.Caching

Namespace CustomCacheSample

    Public Class CustomCache

        Inherits MemoryCache

        Public Sub New()

            MyBase.New("defaultCustomCache")

        End Sub

        Public Overrides Sub Set(ByVal item As CacheItem, _

                ByVal policy As CacheItemPolicy)

            Set(item.Key, item.Value, policy, item.RegionName)

        End Sub

        Public Overrides Sub Set(ByVal key As String, _

                ByVal value As Object, _

                ByVal absoluteExpiration As DateTimeOffset, _

                Optional ByVal regionName As String = Nothing)

            Set(key, value, New CacheItemPolicy _

                With {.AbsoluteExpiration = absoluteExpiration}, _

         regionName)

        End Sub

        Public Overrides Sub Set(ByVal key As String, _

            ByVal value As Object, _

            ByVal policy As CacheItemPolicy, _

            Optional ByVal regionName As String = Nothing)

            MyBase.Set(CreateKeyWithRegion(key, regionName), _

                value, policy)

        End Sub

        Public Overrides Function GetCacheItem(ByVal key As String, _

            Optional ByVal regionName As String = Nothing) As CacheItem

            Dim temporary As CacheItem = _

                MyBase.GetCacheItem(CreateKeyWithRegion(key, regionName))

            Return New CacheItem(key, temporary.Value, regionName)

        End Function

        Public Overrides Function Get(ByVal key As String, _

            Optional ByVal regionName As String = Nothing) As Object

            Return MyBase.Get(CreateKeyWithRegion(key, regionName))

        End Function

        Public Overrides ReadOnly Property DefaultCacheCapabilities() _

                As DefaultCacheCapabilities

            Get

                Return (MyBase.DefaultCacheCapabilities Or _

                    System.Runtime.Caching.DefaultCacheCapabilities.CacheRegions)

            End Get

        End Property

        Private Function CreateKeyWithRegion(ByVal key As String, _

                ByVal region As String) As String

            Return "region:" & (If(region Is Nothing, "null_region", _

                region)) & ";key=" & key

        End Function

    End Class

End Namespace

 

[C#]

using System;

using System.Web;

using System.Runtime.Caching;

namespace CustomCacheSample

{

    public class CustomCache : MemoryCache

    {

        public CustomCache() : base("defaultCustomCache") { }

        public override void Set(CacheItem item, CacheItemPolicy policy)

        {

            Set(item.Key, item.Value, policy, item.RegionName);

        }

        public override void Set(string key, object value, DateTimeOffset absoluteExpiration, string regionName = null)

        {

            Set(key, value, new CacheItemPolicy { AbsoluteExpiration = absoluteExpiration }, regionName);

        }

        public override void Set(string key, object value, CacheItemPolicy policy, string regionName = null)

        {

            base.Set(CreateKeyWithRegion(key, regionName), value, policy);

        }

        public override CacheItem GetCacheItem(string key, string regionName = null)

        {

            CacheItem temporary = base.GetCacheItem(CreateKeyWithRegion(key, regionName));

            return new CacheItem(key, temporary.Value, regionName);

        }

        public override object Get(string key, string regionName = null)

        {

            return base.Get(CreateKeyWithRegion(key, regionName));

        }

        public override DefaultCacheCapabilities DefaultCacheCapabilities

        {

            get

            {

                return (base.DefaultCacheCapabilities | System.Runtime.Caching.DefaultCacheCapabilities.CacheRegions);

            }

        }

        private string CreateKeyWithRegion(string key, string region)

        {

            return "region:" + (region == null ? "null_region" : region) + ";key=" + key;

        }

    }

}

21. Save the file.

Creating the Custom Cache and Caching the Entry

Next, you will instantiate the custom CustomCache class and create a custom memory cache object that adds the cache entry to a region. The code will perform the following tasks:

· Creates an instance of a custom cache class—that is, it instantiates the custom cache object.

· Specifies a named region (a region in the cache to which a cache entry can be added). The path of the text file will not be monitored for changes. This means that the cache entry will not expire when the file is changed because the file path is not monitored for changes.

· Reads the text file and caches its contents as a cache entry.

· Displays the contents of the cached text file.

 

To create an instance of the custom cache object and add a cache entry

22. Switch to or open the Default.aspx file.

23. Double-click the Get from Custom Cache button to create an event handler in the Default.aspx.cs or Default.aspx.vb file.

24. At the top of the file (before the class declaration), add the following Imports (Visual Basic) or using (C#) statements to add the namespace the custom class is in.

[Visual Basic]

Imports CustomCacheSample

 

[C#]

using CustomCacheSample

25. In the class declaration, add the following code to instantiate the custom cache. You must create and keep the reference of the custom cache to prevent it from going out of scope.

[Visual Basic]

Private Static customCache As ObjectCache = _

    New CustomCache()

Private ReadOnly Property RegionCache() As ObjectCache

    Get

        Return customCache

    End Get

End Property

 

[C#]

private static ObjectCache customCache =

    new CustomCache();

private ObjectCache RegionCache { get { return customCache; } }

26. In the event handler, add the following code to read the contents of a cache entry named CustomFilecontents from the RegionCache cache.

[Visual Basic]

Dim CustomFileContents As String = _

    TryCast(RegionCache.[Get](" CustomFileContents ", "Region_A"), String)

 

[C#]

string CustomFileContents = RegionCache.Get("CustomFileContents", "Region_A") as string;

27. Add code to perform the following tasks:

a. Check whether the cache entry named CustomeFilecontents exits. If the specified cache entry does not exist, you must read the text file and add it as a cache entry to the cache.

b. Read the text file

c. Insert the contents of the file into the custom cache object as a cache entry. You specify the named region where the cache entry should be added by passing the region name as parameter in the Set method. You also specify that the cache should not expire based on an absolute time by passing the System.Runtime.Caching.ObjectCache.InfiniteAbsoluteExpiration field as a parameter in the Set method. Instead, items will expire only when there is memory pressure.

The following example shows how to accomplish these tasks

Note   The “Using custom cache” string is added to the text of the text file to help you identify the cached data when you test the page later in this walkthrough.

[Visual Basic]

If CustomFileContents Is Nothing Then

   ' Fetch the file contents.

  Dim cachedFilePath As String = Server.MapPath("~") & _

        "\cacheText2.txt"

    CustomFileContents = File.ReadAllText(cachedFilePath) _

      & " Using custom cache " & + DateTime.Now.ToString()

    ' For this cache region, don't bind a file-change monitor.

    RegionCache.Set("CustomFileContents", CustomFileContents,

          ObjectCache.InfiniteAbsoluteExpiration, "Region_A")

End If

 

[C#]

if (CustomFileContents == null)

{

    //Fetch the file contents

            List<string> filePaths = new List<string>();

            string cachedFilePath = Server.MapPath("~") + "\\cachedText.txt";

   

            filePaths.Add(cachedFilePath);

            CustomFileContents = File.ReadAllText(cachedFilePath) +

           " Using custom cache " + "\n" + DateTime.Now.ToString();

            // For this cache region, don't bind a file-change monitor

            RegionCache.Set("CustomFileContents", CustomFileContents,

            ObjectCache.InfiniteAbsoluteExpiration, "Region_A");

}

28.  Add the following code to display the cached file content in a Label control.

[Visual Basic]

Label1.Text = CustomFileContents

 

[C#]

Label1.Text = CustomFileContents;

29.

Testing Caching in the ASP.NET Web Site

You can now test the application.

 

To test caching in the ASP.NET Web site

30. Press CTRL+F5 to run the application.

31. Click Get from Built-in Cache.

The cached content in the text file is displayed in the label. Notice the "Using built-in cache" statement and the time stamp at end of the file.

32. Click Get from Custom Cache.

The cached content in the text file is displayed in the label. Notice the "Using custom cache" statement at the end of the file.

33. Click Get from Built-in Cache again .

The timestamp is unchanged. This indicates the cached content is displayed.

34. Wait 10 seconds or more and then click Get from Built-in Cache again.

This time a new timestamp is displayed. This indicates that the policy lets the cache expire after 10 seconds and that new cached content is displayed.

35. Click Get from Custom Cache.

The cached content in the text file is displayed in the label. Notice the "Using custom cache" statement and the time stamp at the end of the file.

36. Click Get from Custom Cache again. The timestamp is unchanged. This indicates the cached content is displayed.

37. Wait 10 seconds or more and then click Get from Custom Cache again.

The timestamp is still unchanged. This indicates that the cache does not expire based on a policy. However, when the server starts run low on memory, the cache will automatically expire.

 

- Philip Attipoe
ASP.NET User Education

This posting is provided "AS IS" with no warranties, and confers no rights.

Comments

  • Anonymous
    January 15, 2011
    There is a bug at Step 26: RegionCache.Get("CustomFileContents ",You was append a space character in the key.
  • Anonymous
    January 17, 2011
    Fixed, thanks.
  • Anonymous
    January 18, 2011
    The comment has been removed
  • Anonymous
    January 18, 2011
    Ok, I got the logic behind returning a new Cache Item. Sorry for the above post. Just one more question, on returning the new CacheItem() it wont exist in the base memory cache so there might be a sync issue. Can we return some sort of a readonly CacheItem Object from this method?
  • Anonymous
    February 17, 2011
    isn't this just a naming convention implementation? This won't improve performance when you search for a particular key tagged with region as it would search for that key in entire memory cache rather than corresponding region of the cache..
  • Anonymous
    November 13, 2011
    Thanks for the article, it clearly explains some concepts. Did you submit the article to msdn ?
  • Anonymous
    April 24, 2012
    What if i Need to store/retrieve  my caching data in the sql server 2008
  • Anonymous
    August 06, 2013
    How can I get to specify a named area of all cache entries?like this: in a area "regionName=region_A" have Multiple records,and i only know the "regionName" can i take all the datas?