.NET Framework: Use Your Own Cache Wrapper to Help Performance

Introduction

Welcome to this installment of the .NET Nuts & Bolts column. In this article we’re going to explore a technique that I’ve seen used a number of times to help improve performance of repetitive data retrieval. We’ll explore it from a web application point of view and a traditional windows application point of view. It will involve building some custom objects to hold reference or other lookup data.

Common Data

First we’ll start with an explanation of what I consider to be common data. This doesn’t mean it is the only type of data you could use this for, but rather that it is data that I commonly do use it for. I tend to look for any data that I store as a key value pair in a database where I might want to retrieve it and store it in a dictionary or hashtable object. It is extremely common to store data in an abbreviated form, but yet present it in a different manner. For example, state codes such as IN are commonly stored in the database, but the full Indiana may be used in the display. I also consider application configuration settings to be common data.

Rather than store settings in a web.config or app.config file I tend to store them in a database instead. I prefer it because then I can have a centralized database of all of my application settings and don’t get bitten by deployment issues where the right config wasn’t copied to one of the servers in the web farm. Sure there are tools for this, but when introducing new personnel and doing lots of training in these types of environments the accidents are bound to happen and can be very hard to track down. It also then allows me to have a central admin tool I can use to administer settings from multiple applications and not have to build that stuff in to each application.

Building Your Container

After having identified your common data, the next thing we need to consider is the container to hold it around. I tend to consider myself a pragmatic programmer, meaning, not over and not under engineering things. Below you will find a very basic object that I commonly use for holding application configuration settings. It works for all kinds of stuff. In this case it presents an array that can be indexed by a name, which in turn just goes and checks a hash table for the requested value.

  public class AppSettings
  {
      // This indexer is used to retrieve AppSettings from Memory.
      public string this[string name]
      {
          get
          {
              if (string.IsNullOrEmpty(name))
                 return string.Empty;

                  // Make sure we have an AppSettings cache item loaded
              if (HttpContext.Current.Cache["AppSettings"] == null)
              {
                  App.AppSettings.LoadAppSettings();
              }

              Hashtable ht = (Hashtable)HttpContext.Current.Cache[settingKey];
              if (ht.ContainsKey(name))
              {
                  if (ht[name] != null)
                  {
                      return ht[name].ToString();
                  }
                  return string.Empty;
              }
              else
                  return string.Empty;
          }
      }

      // This Method is used to load the app settings from the database into memory.
      public static void LoadAppSettings()
      {
          Hashtable ht = new Hashtable();

          // Code goes here to get your data from a database.
  	 // my omitted code used LINQ to Entities to populate a results object
          // based on my entity definition

          foreach (var appSetting in results)
          {
              if (ht.ContainsKey(appSetting.vc_Name))
              {
                  ht.Remove(appSetting.vc_Name);
                  ht.Add(appSetting.vc_Name, appSetting.vc_Value
              }
              else
              {
                  ht.Add(appSetting.vc_Name, appSetting.vc_Value);
              }
          }

          // Remove the items from the cache if they are already there
          if (HttpContext.Current.Cache[settingsKey] != null)
              HttpContext.Current.Cache.Remove(settingsKey);

          // Add it into Cache
          HttpContext.Current.Cache.Add(settingsKey,
              ht, null, System.Web.Caching.Cache.NoAbsoluteExpiration,
              new TimeSpan(1, 0, 0),
              System.Web.Caching.CacheItemPriority.NotRemovable, null);
          }
      }
  }

The following example code represents the simple call you would make to get an item from your application settings. You may want to consider making it a static object as well to avoid some slight goofy syntax for when you use it, which you’ll see following the example.

  string siteDisplayName = new AppSettings()["Site.Display.Name"]);

  string redirectURLWithoutWWW = new AppSettings()["Site.URL.Without"]);

Using it in a Windows Application

The example I gave above was implemented from an ASP.NET framework perspective as it used the ASP.NET cache mechanism to persist the data container. Within a Windows application I’ve traditionally just used a static object. In the ASP.NET application you have to worry about the item persisting across round trips to the server, which is why the cache is used since that is the storage mechanism for that data. In a windows application you don’t have that issue and a good old static object should do the trick. An adjustment you could consider when using it for a windows application is to use the WeakReference object to allow your objects to still be cleaned up by the garbage collector if needed, and have a mechanism to pull it again from the source to put it back. This may be another future article topic.

Multiple Tenant

Some additional food for thought around the application settings above are in regard to multiple tenant / instance applications. For example, a web site that presents different faces and data, but under the same code base and hardware would be a multiple instance application. By introducing a tenant / instance ID into the application settings above, we can store all the settings in one central location, but yet keep them separate. It is even possible to use this to build a hierarchy of permissions where there are default values that can easily be overridden in each instance. It’s likely the topic of my next article.

Summary

We have looked at what I consider to be common data and some techniques for caching it to have it readily available in your applications. It can be particularly useful for configuration settings as seen in the highlighted examples.

Future Columns

The topic of the next column is yet to be determined. If you have something else in particular that you would like to see explained here you could reach me through http://markstrawmyer.com.

Related Articles

More by Author

Get the Free Newsletter!

Subscribe to Developer Insider for top news, trends & analysis

Must Read