Custom .NET caching

It turns out that it’s hard to implement caching in class libraries in .NET. The enterprise libraries have a Caching Application Block, but this is nice and light-weight, and it works nicely as a standalone, which the Caching Application Block won’t do for you because it requires a config file. ASP.NET has great support for Web caching in System.Web.Caching, and a few examples on the net actually suggest referencing that assembly in your class libraries or Win Forms apps, but that seems kind of… breaky… so here’s a simple custom cache implementation in C# that handles expiration. It’s not quite as nice as System.Web.Caching, but if you don’t like it, don’t use it.

I use this in a COM visible class library that gets used by a Classic ASP site. Works great.

CacheFactory class

public class CacheFactory
{
    private Hashtable _data;

    public CacheFactory()
    {
        _data = new Hashtable();
    }

    #region properties
    public object this[string key]
    {
        get
        {
            object rtn = null;
            RemoveExpiredItems();
            CacheItem item = (CacheItem)_data[key];
            if (item != null)
            {
                rtn = item.Data;
            }
            return rtn;
        }
    }

    public int Count
    {
        get
        {
            RemoveExpiredItems();
            return _data.Count;
        }
    }

    public ICollection Keys
    {
        get
        {
            RemoveExpiredItems();
            return _data.Keys;
        }
    }
    #endregion

    public void Add(string key, object data)
    {
        Add(key, data, 0);
    }
    public void Add(string key, object data, double minutes)
    {
        DateTime expiresAt;
        if (minutes > 0)
            expiresAt = DateTime.Now.AddMinutes(minutes);
        else
            expiresAt = DateTime.MinValue;
        _data[key] = new CacheItem(key, data, expiresAt);
    }

    public void Remove(string key)
    {
        CacheItem item = (CacheItem)_data[key];
        if (item != null)
            _data.Remove(key);
    }

    public void Clear()
    {
        _data.Clear();
    }

    public DateTime ExpiresAt(string key)
    {
        DateTime rtn = DateTime.MinValue;
        CacheItem item = (CacheItem)_data[key];
        if (item != null)
        {
            rtn = item.ExpiresAt;
        }
        return rtn;
    }

    public string Name(string key)
    {
        RemoveExpiredItems();
        string rtn = null;
        CacheItem item = (CacheItem)_data[key];
        if (item != null)
        {
            rtn = item.Name;
        }
        return rtn;
    }

    public CacheItem GetCacheItem(string key)
    {
        RemoveExpiredItems();
        CacheItem item = (CacheItem)_data[key];
        return item;
    }

    public bool IsExpired(string key)
    {
        bool rtn = false;
        CacheItem item = (CacheItem)_data[key];
        if (item.Expires && DateTime.Now > item.ExpiresAt)
        {
            rtn = true;
        }
        return rtn;
    }

    protected void RemoveExpiredItems()
    {
        CacheItem item;
        DateTime expiresAt = DateTime.MaxValue;
        StringCollection keys = new StringCollection();
        foreach (string key in _data.Keys)
        {
            keys.Add(key);
        }

        foreach (string key in keys)
        {
            item = (CacheItem)_data[key];
            if (item == null || (item.Expires && DateTime.Now > item.ExpiresAt))
                _data.Remove(key);
        }
    }
}

CacheItem class

public class CacheItem
{
    private object _data;
    private DateTime _expiresAt;
    private string _name;

    public CacheItem(string name, object data, DateTime expiresAt)
    {
        _name = name;
        _data = data;
        _expiresAt = expiresAt;
    }

    public object Data
    {
        get
        {
            return _data;
        }
    }

    public DateTime ExpiresAt
    {
        get
        {
            return _expiresAt;
        }
    }

    public string Name
    {
        get
        {
            return _name;
        }
    }

    public bool Expires
    {
        get
        {
            return (ExpiresAt != DateTime.MinValue);
        }
    }
}

References:
http://authors.aspalliance.com/aldotnet/

Advertisements