Cache Management Helper Class

As a follow up to my post on creating a Web Session Management and Centralization Class, I thought I would also touch on one method of presentation level caching. Caching, as well all know, stores a local version of some data closer to the actual requestor making the call, whether it is in the BLL, the presentation, or even page/control specific. Caching can be a significant boost to performance and is fairly easy to implement. Additionally, there are several types of caching, such as image, page, custom, etc. In this brief example, we’re going to explore a custom implementation of caching to minimize trips to the database.

When Would I Use This?

In my opinion the ideal candidates for presentation level caching are things like dropdown lists which are dynamically pulled from the database. I’ve been working on a project which utilizes several dropdowns throughout the application, and each one is to be administered dynamically. This means for each page, I may have several calls to simply setup the page and populate the dropdowns.

In this application, I’m calling a business logic layer which calls down to the data access layer and finally to the database. We can minimize the amount of calls to the database by instead of calling directly to the business layer, call our WebCacheManager class, which is going to first check if the data exists in local cache, or if it needs to make a call to retrieve the data. If the call is made down to the business layer, the results are then stored in the local cache until either the cache expires based on an absolute date/time or a dependent file (acting as a trigger) is modified.

The external file, located in _cacheDependencyFilePath in the following code is a textfile, which can be manually or programmatically modified to expire the cache without having to reset IIS or wait out the expiration time period. The following code sets up a helper class which could sit on the presentation (web application), and be called to retrieve cached data back very quickly.

WebCacheManager

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using Common.Entities.Complaints;  //This is just a reference to my business entities project
using System.Web.Caching;
using System.IO;

namespace UI.ComplaintsWeb.Code
{
    public class WebCacheManager
    {
        /// 
        /// A reference to the current context cache.
        /// 
        private Cache _cache;

        /// 
        /// A filepath to the cachedependency file which triggers expiration of cache.
        /// 
        private string _cacheDependencyFilePath;

        /// 
        /// The absolute date/time of cache expiration.
        /// 
        private DateTime _expirationDate;

        public WebCacheManager()
        {
            _cache = HttpContext.Current.Cache;
            _cacheDependencyFilePath = Path.GetFullPath("../CacheDependency.txt");
            _expirationDate = new DateTime(DateTime.Now.Year, DateTime.Now.Month, DateTime.Now.Day, 23, 59, 59);
        }

        public InspectionType[] GetInspectionTypes()
        {
            InspectionType[] data;
            string cacheKey = "InspectionTypes";

            if (_cache[cacheKey] != null)
            {
                //Get data from cache
                data = _cache[cacheKey] as InspectionType[];
            }
            else
            {
                //Get data from database                
                data = WebHelper.ComplaintsLogic.GetInspectionTypes();

                //Assign to cache
                _cache.Add(cacheKey, data, new CacheDependency(_cacheDependencyFilePath), _expirationDate, System.Web.Caching.Cache.NoSlidingExpiration, CacheItemPriority.Normal, null);
            }

            return data;
        }        
    }
}

Fancy, How Do I Call It?

No sweat, let’s imagine we’re binding to a DropDownList called “InspectionTypeList”, instead of your typically business call (or direct DB call if you’re into that sort of thing, and you probably shouldn’t be, but that’s another topic); simple use the following similar syntax:

var cacheManager = new WebCacheManager();
//Inspection Types
InspectionTypeList.DataSource = cacheManager.GetInspectionTypes();
InspectionTypeList.DataBind();

Not too much to it. If you have any questions on how the WebCacheManager is being used, or suggestions on improvements, please leave a comment below. Don’t be shy, you know you want to comment.

A Generic Data Mapper for Simple Data

Recently, a buddy and I were working on a project and we came across an almost abnormal amount of simple data entities which were used primarily for populating dropdowns. You know the type of data, there is essentially an ID, Name, and not much else.

We needed to do several things with these simple entities, namely populate dropdown, but also associate the entities with other more complex entities. In our architecture, we’re using a layered approach and manual mappers of data retrieval to business entities. There were several ways that we could have gone about propagating this data through the DAL, BLL, and to the presentation, but we chose to create individual entities to represent each of these lists of data. Our reasoning for this was to optimize the ability to separate the code and provide for future classes that could take on additional responsibility if these entities became more complex.

For these simple entities, we didn’t want to write an individual mapper for each one (essentially a whole lot of cut and paste work); so we opted to create a generic mapper class which is constrained by a base class which all of the simple entities inherit from (SimpleData).

The SimpleData class is just that, simple, and looks like this:

The SimpleData Entity

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Runtime.Serialization;

namespace Common.Entities.General
{
    [DataContract()]
    public abstract class SimpleData
    {
        #region Constructors

        public SimpleData()
        {
        }

        public SimpleData(int id)
            : this(id, string.Empty, string.Empty)
        {
        }

        public SimpleData(string text, string value) : this(0, text, value)
        {            
        }

        public SimpleData(int id, string text, string value)
        {
            this.ID = id;
            this.DataText = text;
            this.DataValue = value;
        }

        #endregion

        #region Properties

        public DateTime Created { get; set; }
        public string DataText { get; set; }
        public string DataValue { get; set; }
        public int ID { get; set; }
        public DateTime? LastUpdated { get; set; }

        #endregion

        #region Methods

        public abstract SimpleData Create(int id, string text, string value);

        #endregion
    }
}

Now that we had a base class to inherit from, we needed to apply this to our simple entity to be mapped; here is one example of our entity inheriting SimpleData:

The Entity to be Mapped

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace Common.Entities.General
{
    /// 
    /// An arbitrary class implementing SimpleData.
    /// 
    public class CallType : SimpleData
    {
        #region Constructors

        public CallType()
        {
        }

        public CallType(int id)
            : this(id, string.Empty, string.Empty)
        {
        }

        public CallType(string text, string value) : base(text, value)
        {            
        }

        public CallType(int id, string text, string value)
            : base(id, text, value)
        {
        }

        #endregion

        public override SimpleData Create(int id, string text, string value)
        {
            return new CallType(id, text, value);
        }
    }
}

Now that we had a consistent approach to marking up the simple entities, we could create a single generic mapper class called “SimpleMapper”. The class takes in a DataRow, the name of the ID field, the name of the textual (Name) field, and the name of the value field (specifically, if this is different than the ID).

The reason we have a few extra input parameters here is so the SimpleMapper could handle for these two scenarios (we’ll use our Call Type as the example):

  • Scenario 1: My data has an integer ID for the unique identifier of the database record and the name of the Call Type as the textual value. My database table could look like this:
CallTypes Database Table
ID int
Name varchar(50)
  • Scenario 2: My data has an integer ID for the unique identifier of the database record, a name of the Call Type as the textual value, and a 3-digit textual code for the CallType. My database table could look like this:
CallTypes Database Table
ID int
Name varchar(50)
Code varchar(3)

By specifying which column should be returned as the DataValue field (either the ID, the code, or even the textual name itself) the class has a little more flexibility. Below is the SimpleMapper class in its entirety:

The SimpleMapper Class

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Common.Entities.General;
using System.Data;

namespace DataAccess.Full
{
    public class SimpleMapper where T : SimpleData, new()
    {
        public static T Map(DataRow row, string idName, string textName, string valueName)
        {
            var creator = new T();
            int id = (int)row[idName];
            string text = (string)row[textName];

            string value = id.ToString();
            if (idName != valueName)
            {
                value = (string)row[valueName];
            }

            var obj = creator.Create(id, text, value);
            
            return (T)obj;
        }

        public static T[] Map(DataTable data, string idName, string textName, string valueName)
        {
            var entities = new List();
            foreach (DataRow row in data.Rows)
            {
                entities.Add(Map(row, idName, textName, valueName));
            }

            return entities.ToArray();
        }
    }
}