ASP .Net Caching API Tips

Posted Monday, June 26, 2006 9:46 AM by mhodnick

The ASP .Net web caching API is a powerful and useful tool at your disposal. Here are a few tips and suggestions that will help make your code more flexible and maintainable when using the caching API:

  • Define unique keys for cached items in your domain
  • Store expiration durations or times in a configuration file (web.config)
  • Include a configuration setting for enabling and disabling caching
  • Encapsulate caching behavior in a class

Define Unique Keys

You can get or insert items from and into the cache using a key. A key is a String value that uniquely identifies an item stored in the cache. Normally you will want to use a key based on the unique identifier of some domain object you are working with. For example, you may have a Customer class with an Integer property named CustomerId that uniquely identifies the customer in your domain. Using the CustomerId as the basis for the cache key would be a good choice. However, the unique identifier of the domain object alone is not a good, complete choice for the cache key. Consider this example:

//Bad example.  Non-unique keys are used.
Customer customer = GetCustomerById(100);
Employee employee = GetEmployeeById(100);
double customerValue = 10;
double employeeValue = 100;
Cache.Insert(customer.CustomerID.ToString(), customerValue);
Cache.Insert(employee.EmployeeID.ToString(), employeeValue);

The same cache key ("100") is being used to store two different values for two different types of objects, and the employee's value will overwrite the customer's value. In real life you wouldn't do anything like the example above on purpose, but when dealing with the cache across many web forms or web controls in a web application, you may not know or remember what other keys are being used to cache values.

A better approach would be to qualify the cache key with something that describes the data being stored:

//Better example.  Unique keys are used.
Customer customer = GetCustomerById(100);
Employee employee = GetEmployeeById(100);
double customerValue = 10;
double employeeValue = 100;
Cache.Insert("Customer" + customer.CustomerID.ToString(), customerValue);
Cache.Insert("Employee" + employee.EmployeeID.ToString(), employeeValue);

Make Sliding and Absolute Expirations Configurable

Don't hard-code expiration times in your caching code. Someone (either you or a user) will probably change their mind in the future about how long to store data in the cache. Use a customer configuration section or the AppSettings section in web.config to store cache expiration values, as this will allow you to change cache times without re-compiling the application.

The web.config file could use an appSettings section as simple as the example below to configure the number of minutes to store an item in the cache:

<appSettings>
  <add key="customerYtdSalesCacheMinutes" value="30"/>
</appSettings>

Insert items into the cache using the app setting value:

...
//calculate the absolute expiration DateTime
DateTime absoluteExp =
    DateTime.Now.AddMinutes(YtdSalesCacheMinutes);
//Use a sliding expiration of zero
TimeSpan slidingExp = TimeSpan.Zero;
Cache.Insert(
    someKey, someValue, null, absoluteExp, slidingExp);
...

public int YtdSalesCacheMinutes
{
  get 
  { 
    return Convert.ToInt32(
      ConfigurationManager.AppSettings["customerYtdSalesCacheMinutes"]); 
  }
}

Enable and Disable Caching Through Configuration

There may be times when you'll want to disable caching within your app (e.g. during development or debugging). Use a configuration setting and add some simple logic to your code to handle this:

<!-- web.config -->
<appSettings>
  <add key="customerYtdSalesCacheEnabled" value="true"/>
</appSettings>

// C# code //
...
if (YtdSalesCacheEnabled)
    Cache.Insert(key, myValue, null, absoluteExpiration, slidingExpiration, CacheItemPriority.Normal, null);
...

public bool YtdSalesCacheEnabled
{
    get
    {
        string setting = 
            ConfigurationManager.AppSettings["customerYtdSalesCacheEnabled"];
        if (string.Compare(setting, "true", true) == 0)
            return true;
        return false;
    }
}

Encapsulate Caching Behavior in a Class

Insert and obtain cached values to and from the ASP .Net cache with a class designated for that behavior. This will provide the full benefit of an object-oriented approach. The most obvious and immediate benefits are that caching can be re-used from anywhere within the application, cache keys can be generated consistently, and configuration settings can be obtained in a single place. The design of such a class could take on many different forms to suit the needs of different applications, but here is one example:

using System;
using System.Web.Caching;
using System.Configuration;

namespace MJH.Caching
{
    public class CustomerCacheManager
    {
        private const string CUST_YTD_SALES_KEY = "CustomerYTDSales-{0}";

        /// <summary>
        /// Obtains a customer's YTD sales value.
        /// </summary>
        /// <param name="cache"></param>
        /// <param name="customer"></param>
        /// <returns></returns>
        public double GetCustomerYtdSales(Cache cache, Customer customer)
        {
            //get the cache key
            string key = GetCustYtdKey(customer.CustomerID);
            
            //return the value from the cache if it exists
            if (cache[key] != null)
                return (double)cache[key];

            //calculate the (time consuming) YTD sales value
            double ytdSalesValue = customer.GetYtdSalesValue();

            //if caching is enabled, store the calculated
            //value in the cache
            if (YtdSalesCacheEnabled)
            {
                DateTime absoluteExp = 
                    DateTime.Now.AddMinutes(YtdSalesCacheMinutes);
                TimeSpan slidingExp = TimeSpan.Zero;
                cache.Insert(
                    key,                        //item key
                    ytdSalesValue,              //item value
                    null,                       //dependencies
                    absoluteExp,                //absolute expiration
                    slidingExp);                //sliding expiration
            }
            return ytdSalesValue;
        }

        /// <summary>
        /// Generates a cache key for a customer
        /// YTD sales value.
        /// </summary>
        /// <param name="customerId"></param>
        /// <returns></returns>
        private string GetCustYtdKey(int customerId)
        {
            return string.Format(CUST_YTD_SALES_KEY, customerId.ToString());
        }

        /// <summary>
        /// Gets the nubmer of minutes that YTD sales
        /// values should be cached.
        /// </summary>
        public int YtdSalesCacheMinutes
        {
            get { 
                return Convert.ToInt32(
                    ConfigurationManager.AppSettings["customerYtdSalesCacheMinutes"]); 
            }
        }

        /// <summary>
        /// Gets whether caching is enabled.
        /// </summary>
        public bool YtdSalesCacheEnabled
        {
            get
            {
                string setting = ConfigurationManager.AppSettings["customerYtdSalesCacheEnabled"];
                if (string.Compare(setting, "true", true) == 0)
                    return true;
                return false;
            }
        }
    }
}
Filed under:

Comments

# re: ASP .Net Caching API Tips

Tuesday, February 13, 2007 4:52 PM by Jason

Very Helpful!!

Cache.Insert("Customer" + customer.CustomerID.ToString(), customerValue);

makes so much sense.