Recently I published a video on www.screencastaday.com which introduced the use of custom attributes. One of the viewers emailed me and asked how to use custom attributes in a practical application. In this post I will describe how to create a cache attribute which will cache the return values of the data access layer methods.
I have used the repository pattern in my example. The repository calls the data service layer and fetches the results from the database. Here is the class diagram:

The CacheService is responsible for fetching the cache results. The repository calls the CacheService as shown in the code below:
DateTime result = CacheService.Invoke<ICustomerDataService, DateTime>
(_service, "GetDateTime", null);
I think the above call can be refactored since right now the repository manually has to call the CacheService in order to get the cached results.
The CacheService Invoke method implementation is shown below:
public static TResult Invoke<TSource,TResult>(TSource service,string methodName,object[] parameters)
{
CacheCallAttribute cacheCallAtt = null;
string key = String.Format("{0}_{1}", (string) HttpContext.Current.Session["UniqueId"],
methodName);
MethodInfo method = service.GetType().GetMethod(methodName, BindingFlags.Instance | BindingFlags.DeclaredOnly | BindingFlags.Public);
object[] customAtt = method.GetCustomAttributes(false);
foreach (object obj in customAtt)
{
cacheCallAtt = obj as CacheCallAttribute;
if (cacheCallAtt == null) continue;
object result = method.Invoke(service, parameters);
if (HttpRuntime.Cache[key] == null)
{
HttpRuntime.Cache.Insert(key, result, null, DateTime.Now.AddSeconds(cacheCallAtt.Duration), TimeSpan.Zero);
}
}
if (cacheCallAtt == null) return default(TResult);
return (TResult) HttpRuntime.Cache[key];
}
You will notice that I am injecting the UniqueId from the Session object. This is to make sure that two callers for the same method gets a separate copy for their calls.
The CustomerDataService method is decorated with the CacheCall attribute:
[CacheCall(5)]
public DateTime GetDateTime()
{
return DateTime.Now;
}
You can call this using the ASPX page:
protected void GetTime_Click(object sender, EventArgs e)
{
_service = new CustomerDataService();
_repository = new CustomerRepository(_service);
lblMessage.Text += _repository.GetDateTime() + "<BR>";
}
And here is the result as shown in the screenshot below:

Please note this is not the complete implementation. The CacheService does not cache the parameterized methods.
Download Sample