Tạo Custom Output Cache Provider trên ASP.NET 4

Output Caching is a very common and popular performance improvement technique for ASP.NET applications. In the previous versions of ASP.NET the output caching mechanism of ASP.NET was rigid in terms of cache store. For example, developers didn't have any flexibility if they wanted to store the output cache in a disk file instead of the server memory. Luckily, ASP.NET 4 provides such flexibility to developers. ASP.NET 4 follows a provider model for output caching. This means you can now define a custom cache provider and plug it in using a configuration file. This article illustrates how such a custom output cache provider that stores cached output in disk files can be developed.

Creating a Custom Output Cache Provider

In order to create a custom output cache provider you need to create a class that inherits from the OutputCacheProvider base class. You are then required to override the following methods of the OutputCacheProvider class:

Add()

Remove()

Set()

Get()

The Add() method is intended to add a new output cache entry to the cache store. The cache store is customizable and in our example we will use disk files as the cache store. Remove() method removes a cache entry upon expiration. The Set() and Get() methods assign and retrieve a cache entry respectively. Usage of these methods will be clear when you implement a custom output cache provider in the next sections.

Once a custom output cache provider is ready you need to inform ASP.NET that you wish to use it in your website. You do this by configuring a cache provider in the web.config file.

To begin creating a custom output cache provider, create a new ASP.NET website and add a new class named FileCacheProvider to the App_Code folder. The FileCacheProvider class should inherit the OutputCacheProvider base class and override the methods as discussed earlier. The following code shows the skeleton of FileCacheProvider class.

publicclassFileCacheProvider:OutputCacheProvider

{

publicoverrideobjectAdd(string key,object entry,DateTime utcExpiry)

{

...

}

publicoverridevoidRemove(string key)

{

...

}

publicoverrideobjectGet(string key)

{

...

}

publicoverridevoidSet(string key,object entry,DateTime utcExpiry)

{

...

}

}

Before we override any of the base class method let's create a couple of helper methods that will be used throughout the class. First, add a read-only property CacheLocation as shown below:

The CacheLocation property is supposed to return the physical path of the folder that will be acting as a cache store. To avoid hardcoding of the folder name you store it in the section of the web.config file. The folder path is then retrieved in the CacheLocation property using the ConfigurationManager class. This path is a virtual path (say, for example, ~/Cache) and you need to convert it into a physical path using Server.MapPath() method. A "\" character is appended to the path because a file name will be appended to this base path later.

Next, add a method - GetFullPathForKey() as shown below:

privatestringGetFullPathForKey(string key)

{

string temp = key.Replace('/','$');

returnCacheLocation+ temp;

}

In Add, Remove, Set and Get methods, ASP.NET passes the path of the .aspx file being cached as the cache item key. You cannot use this key directly as a file name because it may contain "/" character(s). To take care of such situations the GetFullPathForKey() method replaces all occurrences of the "/" character with a "$" character and appends it to the CacheLocation. This way you arrive at a valid file name that can be used to persist cached data.

NOTE: The above implementation can be further made foolproof by adding checks for other invalid characters in addition to /. For the sake of this article, however, we will use the simple implementation as shown above.

Next, add Set() method as shown below:

publicoverridevoidSet(string key,object entry,DateTime utcExpiry)

{

string filePath =GetFullPathForKey(key);

CacheItem item =newCacheItem{Expiry= utcExpiry,Item= entry };

FileStream fileStream =File.OpenWrite(filePath);

BinaryFormatter formatter =newBinaryFormatter();

formatter.Serialize(fileStream, item);

fileStream.Close();

}

The Set() method is intended to store an item in cache store. It receives a key for the item being cached, the item to be cached and its expiry date and time. Inside, it serializes an item to be cached into a file using BinaryFormatter. Notice the use of CacheItem class. The CaheItem class is a simple serializable class intended to store the item to be cached and its expiration date. It looks like this:

[Serializable]

publicclassCacheItem

{

publicobjectItem{get;set;}

publicDateTimeExpiry{get;set;}

}

Make sure to mark the class with [Serializable] attribute because you wish to store (serialize) its contents to a file using BinaryFormatter. Now add the Get() method as shown below:

publicoverrideobjectGet(string key)

{

string filePath =GetFullPathForKey(key);

if(!File.Exists(filePath))

{

returnnull;

}

CacheItem item =null;

FileStream fileStream =File.OpenRead(filePath);

BinaryFormatter formatter =newBinaryFormatter();

item =(CacheItem)formatter.Deserialize(fileStream);

fileStream.Close();

if(item ==null|| item.Expiry<=DateTime.UtcNow)

{

Remove(key);

returnnull;

}

return item.Item;

}

Get() method deserializes a previously stored CacheItem using a BinaryFormatter. It also does the job of expiring a cached item based on the expiration date and time. Next, override the Add() method as shown below:

publicoverrideobjectAdd(string key,object entry,DateTime utcExpiry)

{

object obj =this.Get(key);

if(obj !=null)

{

return obj;

}

else

{

this.Set(key, entry, utcExpiry);

return entry;

B }

}

Add() method works similar to Set() method with one difference. If an item is already cached, Add() method will not overwrite the entry whereas Set() method will overwrite it even if it is already present. Add() method receives a key for the item being cached, the item to be cached and its expiry date and time. Finally, the Remove() method simply deletes the physical file and is shown next:

publicoverridevoidRemove(string key)

{

string filePath =GetFullPathForKey(key);

if(File.Exists(filePath))

{

File.Delete(filePath);

}

}

Configuring Your Website to Use Custom Output Cache Provider

Now that you are ready with a custom output cache provider, let's configure the website so that ASP.NET knows that your custom provider is to be used instead of the in-built mechanism. Open web.config and add an section as shown below:

key="CacheLocation"value="~/Cache"/>

Here, you added CacheLocation key to the section. Recollect that CacheLocation key is used in the CacheLocation property of the FileCacheProvider class. In the above example, the folder in which cache files are stored is Cache. Make sure to change the folder name as per your setup. Next, add < caching> section as follows:

defaultProvider="FileCache">

name="FileCache"type="FileCacheProvider"/>

The section adds your custom provider using the element. The type attribute should point to the fully qualified name of the custom cache provider class. If your custom cache provider class is part of a class library rather than App_Code folder you should set the type attribute like this:

name="FileCache"type="MyNamespace.FileCacheProvider, MyAssembly"/>

Make sure to replace namespace and assembly name as per your setup.

If you have added multiple cache providers for the sake of testing, make sure that the defaultProvider attribute of the element points to the correct cache provider.

Sample Run of a Web Form

Now you are ready to use the custom cache provider in web forms. Add a new web form to the website and place a Label control on it. Enable output caching for the web form using @OutputCache directive.

OutputCacheVaryByParam="None"Duration="60" %>

As you can see the cache duration is set to 60 seconds. In the Page_Load event simply display the current time value in the Label.

protectedvoidPage_Load(object sender,EventArgs e)

{

Label1.Text=DateTime.Now.ToShortTimeString();

}

If you run the web form you will find that its output is being cached for the specified duration (as indicated by the time value outputted in the Label). You will also find files being created in the ~/Cache folder (see below).

Figure 1: The ~/Cache folder

Summary

ASP.NET 4.0 allows you to customize output cache store. It follows the provider model and allows you to create a custom output cache provider. A custom output cache provider is essentially a class that derives from OutputCacheProvider base class. You need to override Add(), Remove(), Set() and Get() methods to get the page output in a required cache store. The custom output cache provider is then specified with the help of the configuration section. In the example discussed in this article you used physical disk files to store output cache but you could have used any other storage mechanism.

About the Author

Bipin Joshi

Bipin Joshi is a blogger and writes about apparently unrelated topics - Yoga & technology! A former Software Consultant by profession, Bipin has been programming since 1995 and has been working with the .NET framework ever since its inception. He has authored or co-authored half a dozen books and numerous articles on .NET technologies. He has also penned a few books on Yoga. He was a well known technology author, trainer and an active member of Microsoft developer community before he decided to take a backseat from the mainstream IT circle and dedicate himself completely to spiritual path. Having embraced Yoga way of life he now codes for fun and writes on his blogs. He can also be reached there.