Introduction
This tip will explain approaches to improve an ASP.NET Web application's performance. Application's performance is an important requirement/consideration while designing any application especially when there are going to be 100s and 1000s of requests coming through every second. This tip will introduce some of the ASP.NET framework provided features to help enhance application's performance.
Background
In general, an application’s performance can be improved by saving frequently retrieved data in memory especially when the data was generated as a result of complex programming logic and retrieved from a database or from a file. Saving the retrieved data in memory and displaying the in memory data during successive requests instead of re-performing the complex logic, would enormously improve application’s performance and as well the user experience. For this purpose, ASP.NET framework provides two basic caching mechanisms:
- Application Caching
- Output Caching
In this tip, we will see how performance can be improved through application caching. In my next article, we will discuss about Output Caching and its advantages.
Application Caching
Application caching can be implemented using ASP.NET’s Cache
class. This class belongs to System.Web.Caching
namespace.
Cache items added to the Cache
object are private
to each application, similar to application state values. Its lifetime is dependent on the application itself, so if the application restarts, the Cache
objects will get re-created.
It is very easy to use this Cache
class. We will now see how we can add and retrieve items to Cache
object and thereby improve application’s performance.
Adding Items to Cache
Only one instance of this Cache
object is created for each application domain, and it remains valid as long as the application is active. This object can be accessed either through the HttpContext
object or the Cache
property of the Page
object. Cache
object is a key value pair collection and items can be added to Cache
in three different ways:
1. Setting the Cache Item Via Key and a Value
Cache[“Key1”] = “Value1”;
The above statement has added an item called “Key1
”to the Cache
object and its value has been set to “Value1
”. The above example sets the cache item “Key1
” with a string
, but it can be of any object type.
2. Using the Insert Method
The Insert
method has 5 overloaded versions.
Let us see an example of retrieving data from an XML file and caching this data unless the file’s content is altered. Let us assume that we have the following weather related data in an XML file:
="1.0" ="utf-8"
<ArrayOfObservation>
<Observation>
<Station>Liverpool</Station>
<DateTime>2010-09-01T00:00:00</DateTime>
<Temparature>28.52944896953</Temparature>
</Observation>
<Observation>
<Station>Holsworthy</Station>
<DateTime>2010-09-01T00:00:00</DateTime>
<Temparature>30.68702763071</Temparature>
</Observation>
<Observation>
<Station>ChippingNorton</Station>
<DateTime>2010-09-01T00:00:00</DateTime>
<Temparature>29.52944896953</Temparature>
</Observation>
<Observation>
<Station>Padstow</Station>
<DateTime>2010-09-01T00:00:00</DateTime>
<Temparature>31.68702763071</Temparature>
</Observation>
</ArrayOfObservation>
To keep it simple, the above file contains weather data for just 4 different suburbs, each record contains Station Name, DateTime and the Temparature details. Let us assume that we would like to present this data in a grid view. Once the data is read from the file, we can data bind it to a GridView
control. This data might get updated few times during the day. Instead of reading from the XML file every time a user requests for this data, we could cache the data in the Application’s Cache
object and can invalidate it and repopulate it only if the contents of the file have been updated. The following code snippet shows how to use this type of File Dependency:
private void LoadWeatherData()
{
if (Cache["WeatherData"] == null)
{
DataSet ds = new DataSet();
string weatherDataFile =
HttpContext.Current.Server.MapPath (@"~\data\WeatherData.xml");
ds.ReadXml(weatherDataFile);
GridView1.DataSource = ds;
GridView1.DataBind();
CacheDependency cd = new CacheDependency(weatherDataFile);
Cache.Insert("WeatherData", ds, cd);
}
else
{
GridView1.DataSource = (DataSet)Cache.Get("WeatherData");
GridView1.DataBind();
}
}
Once the item is cached, until, the contents of the file are updated, or the .NET Framework decides to remove the item from the Cache for reasons like low memory, or if the user has explicitly removed the item from the cache, every request to this data will be retrieved from the Cache. Whenever the data in the XML file is updated or the item is removed from the Cache, then the LoadWeatherData
method will read from the file and add the item again to the Cache. Though the above example doesn’t involve any complex logic, in places where we might need complex business logic to be used on the data read from the file before presenting it to the user, the caching mechanism would eliminate the reprocessing of the data and would help presenting the data straight from the cache and thus would significantly improve performance.
Note: The above example uses CacheDependency
on a file to update the Cache. If the data is retrieved from a database, then we could use SqlCacheDependency
mechanism to enable Cache
item to be updated when the underlying data table gets updated. We will discuss about this in my future article.
There is another overloaded version of this Insert
method which takes a delegate as a parameter (CacheItemRemovedCallback
), which gets notified, when the cached data is removed from the Cache
object.
The following code snippet shows how the CacheItemRemovedCallback
delegate can be used: (The signature of this delegate can be checked from Visual Studio by highlighting this class name and using the “Go To Definition” option from the context menu popup).
The above code checks whether an item called “WeatherData
” exists in the Cache
object. If it doesn’t exist, then it reads from the XML file and binds the data to the GridView
Controller and it also adds the item to the Cache
collection. Else it simply binds the data in the Cache
object to the GridView
Controller.
private void LoadWeatherData()
{
DataSet ds = new DataSet();
string weatherDataFile = HttpContext.Current.Server.MapPath(@"~\data\WeatherData.xml");
ds.ReadXml(weatherDataFile);
GridView1.DataSource = ds;
GridView1.DataBind();
CacheDependency cd = new CacheDependency(weatherDataFile);
System.Web.Caching.CacheItemRemovedCallback cacheItemRemovedCallback =
new CacheItemRemovedCallback (onCacheItemRemoved);
Cache.Insert("WeatherData", ds, null, System.DateTime.Now.AddSeconds(60),
System.Web.Caching.Cache.NoSlidingExpiration, CacheItemPriority.Default,
cacheItemRemovedCallback);
}
public void onCacheItemRemoved(string key, object value, CacheItemRemovedReason reason)
{
}
In addition to the above Insert
methods, there are few simple overloaded versions which can be used to either set an absolute expiration time, or a sliding expiration time. Note, both absolute and sliding expiration options can’t be used at the same time.
The following example shows how the inserted item can be cached for a minute using absolute expiration:
HttpContext.Current.Cache.Insert("WeatherData", ds, null,
DateTime.Now.AddSeconds(60),
System.Web.Caching.Cache.NoSlidingExpiration);
The above statement will remove the Cache
item “WeatherData
” from the cache after 1 minute. If we want to set a sliding expiration, then the Absolute expiration
should be set to System.Web.Caching.Cache.NoAbsoluteExpiration
as shown below:
HttpContext.Current.Cache.Insert("WeatherData ", ds, null,
System.Web.Caching.Cache.NoAbsoluteExpiration, new TimeSpan(0, 5, 0));
Though we can Cache
items in the HttpContext.Current.Cache
object, it is not always guaranteed that the Cache
item will remain in the Cache
object for the predefined time. If the application’s memory is running low, then the .NET framework could decide to remove some of the Cached items. The Cached items can be set with a priority value as shown in the following example:
HttpContext.Current.Cache.Insert("WeatherData",
ds, null, System.Web.Caching.Cache.NoAbsoluteExpiration, new TimeSpan(0, 5, 0),
System.Web.Caching.CacheItemPriority.NotRemovable, null);
When the priority is set to NotRemovable
, then this item will not be removed from the Cache
as compared to others even under circumstances where .NET framework decides to remove one or more items from the Cache
. But if the Cache
item had an expiry time set or if it is dependent on another Cache
or file, then it will be removed under such circumstances. The other possible priority values are: Low
, Normal
, BelowNormal
, AboveNormal
, High
, Default
.
3. Using the Add Method
The Add
method can also be used instead of Insert
method and the Add
method also takes all of the above parameters, but there are no overloaded methods which take less parameter as compared to Insert
method. As well, the Add
method will return an instance of the added Cache
object whereas the Insert
method wouldn’t. Removing Items from Cache
to remove or delete an item from the Cache
explicitly, we can use Remove
method as follows:
Cache.Remove(“WeatherData”);
The above method would remove the Cache
item “WeatherData
” from the Cache
permanently.