Cache Util and ServiceBase for SubSonic RepositoryRecord items

I have started working on a project in my “free” time and want to share some of the things I’ve been playing with.  For the new project, I’m planning on using Mvc, SubSonic, Linq, and jQuery to name a few technologies.  I’ll release more info about the project as it shapes into something more tangible.

As I mentioned, I’m using SubSonic with this project and more specifically, I’ve been using what will be SubSonic 2.1.  I’ve been quite happy with the work Rob Conery and his team have done with SubSonic.  For this project, I chose to go with the RepositoryRecord base class rather than ActiveRecord for my objects.  My reasoning was that I end up using services to interact with the objects anyway, so I might as well reduce the “weight” of the objects.  The services that I use add basic object caching as well as hide SubSonic integration.

In order to facilitate caching RepositoryRecord items, I had to rewrite my CacheUtil class slightly:

public class CacheUtil
{
public static List<T> FetchAll<T>() where T : RepositoryRecord<T>, new()
{
string key = typeof(T).ToString();
object item = HttpContext.Current.Cache[key];

if (item == null)
{
List<T> collection = DB.Select()
.From<T>()
.ExecuteTypedList<T>();

HttpContext.Current.Cache.Insert(key, collection);
return collection;
}
return (List<T>)item;
}

public static List<T> FetchAllAsc<T>(params string[] columns) where T : RepositoryRecord<T>, new()
{
string key = typeof(T).ToString() + "__SortAsc";
foreach (string column in columns)
{
key += "_" + column;
}
object item = HttpContext.Current.Cache[key];

if (item == null)
{
List<T> collection = DB.Select()
.From<T>()
.OrderAsc(columns)
.ExecuteTypedList<T>();

HttpContext.Current.Cache.Insert(key, collection);
return collection;
}
return (List<T>)item;
}


public static List<T> FetchAllDesc<T>(params string[] columns) where T : RepositoryRecord<T>, new()
{
string key = typeof(T).ToString() + "__SortDesc";
foreach (string column in columns)
{
key += "_" + column;
}
object item = HttpContext.Current.Cache[key];

if (item == null)
{
List<T> collection = DB.Select()
.From<T>()
.OrderDesc(columns)
.ExecuteTypedList<T>();

HttpContext.Current.Cache.Insert(key, collection);
return collection;
}
return (List<T>)item;
}

public static void ClearCache<T>()
{
string baseCacheKey = typeof(T).ToString();
if (HttpContext.Current.Cache[baseCacheKey] != null)
{
HttpContext.Current.Cache.Remove(baseCacheKey);
}

RemoveWithWildcards(baseCacheKey + "__SortAsc*");
RemoveWithWildcards(baseCacheKey + "__SortDesc*");
}

/// <summary>
/// Removes items from the cache using wildcards * and ?
/// </summary>
/// <param name="pattern">Pattern to search cache keys</param>
public static void RemoveWithWildcards(string pattern)
{
pattern = pattern.Replace("*", @"w*").Replace("?", @"w");
// Only match whole words
pattern = string.Format("{0}{1}{0}", @"b", pattern);
RemoveByPattern(pattern);
}

/// <summary>
/// Removes items from the cache based on the specified regular expression pattern
/// </summary>
/// <param name="pattern">Regular expression pattern to search cache keys</param>
public static void RemoveByPattern(string pattern)
{
IDictionaryEnumerator enumerator = HttpContext.Current.Cache.GetEnumerator();
Regex regex = new Regex(pattern, RegexOptions.Singleline | RegexOptions.Compiled | RegexOptions.IgnoreCase);
while (enumerator.MoveNext())
{
if (regex.IsMatch(enumerator.Key.ToString()))
{
HttpContext.Current.Cache.Remove(enumerator.Key.ToString());
}
}
}
}

 

I’m using the following base class for my services.  While it’s likely to change, I think it’s a pretty good place to start.

public abstract class ServiceBase<T> where T : RepositoryRecord<T>, new()
{
public static void Save(T item)
{
Save(item, "");
}

public static void Save(T item, string username)
{
DB.Save(item, username);
ClearCache();
}

public static void Delete(T item)
{
DB.Delete(item);
ClearCache();
}

public static void Destroy(T item)
{
DB.Destroy(item);
ClearCache();
}

public static T FetchById(object id)
{
List<T> items = FetchAll();
return items.Find(delegate(T item) { return item.GetPrimaryKeyValue().Equals(id); });
}

public static List<T> FetchAll()
{
return CacheUtil.FetchAll<T>();
}

public static List<T> FetchAllAsc(params string[] columns)
{
return CacheUtil.FetchAllAsc<T>(columns);
}

public static List<T> FetchAllDesc(params string[] columns)
{
return CacheUtil.FetchAllDesc<T>(columns);
}

public static void ClearCache()
{
CacheUtil.ClearCache<T>();
}
}