I've recently discovered Context in .Net while reading the Programming .Net Components book (Chapter 11). This is an amazing piece of technology that I definitely needed to try. A first try was to make a Caching context:
Caching context
Image that you have methods that perform long computation, such as querying a database etc..., and return a result. Usually, the result does not change much over time, so typically you would like to cache the results in order to improve the efficiency of the application. Now imagine that there exists a Caching context that would take care of caching method calls. For example, we would like to write something like this:
[Caching]
public class CachedClass : ContextBoundObject
{
[Cached]
public string ReturnBigObject()
{
Console.WriteLine("miss");
Thread.Sleep(1000);
return DateTime.Now.ToString();
}
}
In that sample, we would like the output of ReturnBigObject to be cached. For example, the sample method below shows the desired behavior. The first call to ReturnBigObject is a cache miss and then, the remaining calls are cached.
miss
c.ReturnBigObject(): 5/10/2004 19:31:31
c.ReturnBigObject(): 5/10/2004 19:31:31
c.ReturnBigObject(): 5/10/2004 19:31:31
Let's see the steps to take to create the caching context:
Building the context (1): CachingAttribute
The CachingAttribute has to inherit from ContextAttribute and override two methods. The main task of the attribute is to add a IContextProperty implementation to the context properties (GetPropertiesForNewContext method).
[AttributeUsage(AttributeTargets.Class,AllowMultiple =false,Inherited =true)]
public class CachingAttribute : ContextAttribute
{
public CachingAttribute():base("Chaching")
{}
public override void GetPropertiesForNewContext(IConstructionCallMessage ctorMsg)
{
IContextProperty property =
new CachingContextProperty(ctorMsg.ActivationType);
ctorMsg.ContextProperties.Add(property);
}
public override bool IsContextOK(Context ctx, IConstructionCallMessage ctorMsg)
{
return false;
}
}
Building the context (1.1): CachedAttribute
The CachedAttribute is used to tag method to be cached. It contains several parameters to set up the HttpRuntime.Cache object.
Building the context (2): CachingContextProperty
The task of this class is to install a server IMessageSink in the message flow that will cache the message calls. Therefore, this class implements IContextProperty, IContributeServerContextSink.
public class CachingContextProperty :
IContextProperty, IContributeServerContextSink
{
private Type activationType;
...
public IMessageSink GetServerContextSink(IMessageSink nextSink)
{
CachingSink cachingSink = new CachingSink(nextSink, activationType);
return cachingSink;
}
}
Building the context (3): CachingSink
This is where the real work occurs. This class filters the call to the method (IMethodMessage) and looks in a Cache (HttpRuntime.Cache) if it is stored, if stored it returns the value, otherwize it calls the next sink and store the value in the Cache. (This part of the code is a little bit more technical because a part of the IMethodReturnMessage has to be "copied").
The heart of this class is as follows:
public IMethodReturnMessage SyncProcessMessage(IMethodMessage msg)
{
// creating unique hash value out of method, instance and paramteres
string hash = Hash(msg);
// looking in the cache
IMethodReturnMessage returnMessage = HttpRuntime.Cache[hash] as IMethodReturnMessage;
if (returnMessage != null) // cache hit
return returnMessage;
returnMessage = this.Parent.NextSink.SyncProcessMessage(msg)
as IMethodReturnMessage;
// caching returned information
CachedMethodReturnMessage cachedMessage= new CachedMethodReturnMessage(returnMessage);
// storing in cache
HttpRuntime.Cache.Add(hash, cachedMessage,
this.cachedAttribute.Dependencies,
this.cachedAttribute.AbsoluteExpiration,
this.cachedAttribute.SlidingExpiration,
this.cachedAttribute.Priority,
null);
IMethodReturnMessage r = HttpRuntime.Cache[hash] as IMethodReturnMessage;
return returnMessage;
}
Downloads
The full source is available on www.dotnetwiki.org