Thursday, September 08, 2005

Caching Manager in ASP.Net

Caching SQL Server 2000 Data using ASP.Net

Caching in ASP.Net

In classic ASP, one of the techniques developers commonly relied on to speed up processing was the use of caching.

The main benefits of caching are performance-related: operations like accessing database information can be one of the most expensive operations of an ASP page's life cycle. If the database information is fairly static, this database-information can be cached. When information is cached, it stays cached either indefinitely, until some relative time, or until some absolute time. Most commonly, information is cached for a relative time frame. That is, our database information may be fairly static, updated just a few times a week. Therefore, we might want to invalidate the cache every other day, meaning every other day the cached content is rebuilt from the database.

While caching in classic ASP was a bit of a chore, it is quite easy in ASP.NET. There are a number of classes in the .NET Framework designed to aid with caching information.

Caching options in ASP.Net
· Page Level/Output Caching
· Page Fragment/Partial Page Output Caching
· Programmatic/Data Caching

We discuss how Data Caching can be used to cache SQL Server 2000 Data using ASP.Net in following section.

Data Caching

This is in-memory cache. ASP.NET provides this power and flexibility by providing a cache engine. Programmatic or data caching takes advantage of the .NET Runtime cache engine to store any data or object between responses. That is, you can store objects into a cache, similar to the storing of objects in Application scope in classic ASP. (As with classic ASP, do not store open database connections in the cache!)

Realize that this data cache is kept in memory and "lives" as long as the host application does. In other words, when the ASP.NET application using data caching is restarted, the cache is destroyed and recreated. Data Caching is almost as easy to use as Output Caching or Fragment caching: you simply interact with it as you would any simple dictionary object. To store a value in the cache, use syntax like this:

Cache["foo"] = bar;

To retrieve a value, simply reverse the syntax like this:

bar = Cache["foo"];

Note that after you retrieve a cache value in the above manner you should first verify that the cache value is not null prior to doing something with the data. Since Data Caching uses an in-memory cache, there are times when cache elements may need to be evicted. That is, if there is not enough memory and you attempt to insert something new into the cache, something else has gotta go! The Data Cache engine does all of this scavenging for your behind the scenes, of course. However, don't forget that you should always check to ensure that the cache value is there before using it. This is fairly simply to do - just check to ensure that the value isn't null/Nothing. If it is, then you need to dynamically retrieve the object and restore it into the cache.

//Try to read the cache entry MyString into str
str = Cache("myString");

//Check if str is Nothing
If str == null
{
//If it is, populate str from SetStringToSomething()
str = GetStringFromDB();

'Now insert str into the cache entry myString
Cache("myString") = str;
}

Additionally, you can have an item evicted from the cache when a file changes. Such an eviction dependency is called a file dependency, and has many real-world applications, especially when working with XML files. For example, if you want to pull data out of an XML file, but you don't want to constantly go to disk to read the data, you can tell the ASP.NET caching engine to expire the cached XML file whenever the XML file on disk is changed. To do this, use the following syntax:

Cache.Insert("foo", bar, new CacheDependancy(Server.MapPath("BarData.xml")))

Taking advantage of this Cache File Dependency Property we can design a caching method which will update the cache only when the data in the database table has really changed, so that we get maximum performance.

Caching Manager

The cache will be dependent on a file in the server. i.e. cache gets expired whenever the file changes. So now the file should change when ever a DML statement is executed on the table. For this we have to write a trigger for each table to be kept in the cache so that whenever a new row is inserted, a row is updated or deleted the file corresponding to that table changes.

The trigger

CREATE TRIGGER
WriteCacheDepFile
ON Contacts
FOR INSERT, UPDATE, DELETE
AS
EXEC sp_makewebtask '\\anandah\C$\Cache\mycache.txt',
'SELECT top 1 FirstName FROM contacts'

We need to actually create the directory, and make it a share. You may also need to update permissions so the file can be written. Note that I've used the Administrative share "C$". You may also need to create an initial blank file, "mycache.txt".
This trigger updates the file mycache.txt whenever the Insert, update or delete gets executed.

The ASP.net application

Now we are all ready to create our app. First, let's put the dependency file into our web.config so it's easy to change without redeployment:

In web.config, near the bottom, add an appSettings section like so:








Now, let's set up our Cache mechanism in our Global class so we won't need any page specific code:

Public Class Global: System.Web.HttpApplication

Public void Application_Start(ByVal sender As Object, ByVal e As EventArgs)
{

RefreshCache();
}

Public RefreshCache()
{
CacheItemRemovedCallback onRemove;
onRemove = new CacheItemRemovedCallback(this.RefreshCache());


System.Web.HttpContext.Current.Cache.Insert(“Table1”, GetTable1FromDB(), New CacheDependency(depFile), Cache.NoAbsoluteExpiration, Cache.NoSlidingExpiration, CacheItemPriority.High, onRemove));
}

Public void Session_Start(ByVal sender As Object, ByVal e As EventArgs)
{
If (HttpContext.Current.Cache("Table1") == null )
RefreshCache();
}

Using the OnRemove delegate as shown above we avoid the developer from calling the refreshcache every time it expires. So the Cache management is invisible to developers.


From application we need to just bind the controls to the Cache

Example:

RefreshDataSet()
{
DataGridTable1 = (type conversion accordingly)Cache[“Table1”];
DataGridTable1.DataBinb();
}