Unity v1.2 - Now with method interception

A couple of weeks ago I noticed that the latest Unity release (1.2) has facilities for doing method-level inception so I decided to have a play.

If you haven't seen it before, method-level interception is a way of injecting code into a method call (either before the call or after it returns). Some tools allow you to do this at compile-time (check out PostSharp) and others do it dynamically at runtime. The technique allows you to perform Aspect Oriented Programming, where you extract infrastructure (non-business-related) code into modules and then inject them wherever they are required.

It sounds far more confusing than it really is so in my usual fashion, I'll try to explain by example. Imagine that I have a class in my system for managing a Sitemap (I know that there is one in the framework anyway but we are going to write our own for the example). Here is an implementation that simulates talking to a database by making calls to Thread.Sleep(...):

public class MySitemap
{
private string homeName;

public string Home
{
get
{
// Reading from the database and this takes a long time
Thread.Sleep(65);
return homeName;
}
set
{
// Writing back to the database. Man this is slow
Thread.Sleep(120);
homeName = value;
}
}
}

OK so it's not so much a map as it is a point but it's enough for our purposes. As this is a concrete type we don't need to register anything with the container in order to create one of these and play with it but to make things interesting lets get the container to set the Home property to HOME when it creates an object:

container = new UnityContainer();

container.Configure<InjectedMembers>()
.ConfigureInjectionFor<MySitemap>(
new InjectionProperty("Home", "HOME")
);

Now we can ask the container for our Sitemap and manipulate it to our hearts content. To simulate some heavy traffic lets print out the name of the Homepage 1000 times (we are building a busy site after all):

var sitemap = container.Resolve<MySitemap>();

for (int i = 0; i < 1000; i++)
{
Console.WriteLine(sitemap.Home);
}

That works a treat but we now realize that we're going to have a problem. If it's going to take 65 milliseconds to get the name of the Homepage then it takes 65000 milliseconds to do 1000 (that's over a minute). Even though our flashy web-server is able to process many requests in parallel that is not going to be acceptable and realistically the data rarely changes and we are incurring a database hit each and every time.


What we want to do is add some caching into the code so that only the first request goes to the database (and fills in the cache) and every subsequent request comes out of the cache. There are a few ways we could go about this. Firstly, we could simply write the caching code directly into our consumer class. In the context of our sample the cache could just be a local variable like this:

var sitemap = container.Resolve<MySitemap>();
var home = sitemap.Home;

for (int i = 0; i < 1000; i++)
{
Console.WriteLine(home);
}

That's got us down to about 540 milliseconds but it doesn't really scale very well. For starters our sample code handles 1000 calls, one after the other and all at once whereas in real life we are probably going to be handling them all in parallel and all at different times. It also needs to be implemented everywhere that we use the MySitemap class and request the Home property on it. There is a chance that someone may forget to do this and they'll have to profile their code to figure our where all that time is being wasted.


Our second option is to cache the value when we retrieve it from the database. Imagine that we have an ICachingService that allows us access to some cache (we don't care where, that's the point of the interface) then we can modify our class to accept an ICachineService and use it store and retrieve values:

public class MySitemap
{
private string homeName;

private readonly ICachingService cache;
public MySitemap(ICachingService cache)
{
this.cache = cache;
}

public string Home
{
get
{
if (cache.Contains("cache://sitemap.home") == false)
{
// Reading from the database and this takes a long time
Thread.Sleep(65);
// Cache the returned value to make things faster next time
cache.Store("cache://sitemap.home", homeName);
}
return (string)cache.Retrieve("cache://sitemap.home");
}
set
{
// Writing back to the database. Man this is slow
Thread.Sleep(120);
homeName = value;
// we should also clear the cache out
cache.Remove("cache://sitemap.home");
}
}
}

Now our Sitemap performs cache management for us but its original functionality is a little obscured by the caching code. In fact, if you think about it I'm sure you'll realize that Sitemaps and Caches really have nothing to do with each other and this class is doing too much. We could solve this in a few ways. We could create  a sub-class of MySitemap (called MyCachingSitemap) that only does the caching part and delegates to it's base class to do the database management. 


Once you've done this a few times (MyCachingSitemap, MyCachingPageInfoService, MyCachingForumTopicController) you start to realize that the code you are writing each time is a little repetitive. Wouldn't it be great if you had a magic genie and you could say : "These 6 properties need to interact with the cache. Make me a subclass that implements that behaviour"? Well that's exactly what the method interception extension for Unity does. First lets see how it looks and then we'll delve into how it works:

public class MySitemap
{
private string homeName;

public virtual string Home
{
[Cached("cache://sitemap.home")]
get
{
// Reading from the database and this takes a long time
Thread.Sleep(65);
return homeName;
}
[ClearsCache("cache://sitemap.home")]
set
{
// Writing back to the database. Man this is slow
Thread.Sleep(120);
homeName = value;
}
}
}

This looks almost exactly like our original class except that we've added in some Cache-specific attributes and made our property virtual. The last thing that we need to do is add the InterceptionExtension to our untiy container and configure an Interceptor for MySitemap. There are a few ways in which method calls can get intercepted. The interceptor that I'm using here (VirtualMethodInterceptor) will generate a class at runtime which inherits from our class and overrides all of the virtual methods to provide interception.

container.AddNewExtension<Interception>()
.Configure<Interception>()
.SetDefaultInterceptorFor<MySitemap>(new VirtualMethodInterceptor());

This gets us back up to about 630 milliseconds but it keeps all of caching details abstracted away and is highly reusable. The default policy for Unity when looking for methods to intercept is to look for attributes that inherit from the abstract HandlerAttribute. The base class has a single factory method which returns a class that implements ICallHandler. Presuming we have a CachedCallHandler class that has a property for the cache key our CacheAttribute will be pretty simple. It will look like this (Note that the CreateHandler call gets a copy of the container which it can use to get the call-handler):

class CachedAttribute : HandlerAttribute
{
public override ICallHandler CreateHandler(Microsoft.Practices.Unity.IUnityContainer container)
{
var cachedCallHandler = container.Resolve<CachedCallHandler>();
cachedCallHandler.Key = this.Key;
return cachedCallHandler;
}

public CachedAttribute(string key)
{
Key = key;
}

public string Key { get; protected set; }
}

The last piece of the puzzle is the actual CallHandler that implements the intercepted functionality. Basically what happens is that the Interception mechanism will wrap up all of the meta-data about the method call (it's parameters, return value, any exceptions that have been thrown) into an object that implements the IMethodInvocation interface and passes that into your CallHandler.Invoke method. Here's the entire CachedCallHandler class:

class CachedCallHandler : ICallHandler
{
private readonly ICachingService cachingService;

public CachedCallHandler(ICachingService cachingService)
{
this.cachingService = cachingService;
}

#region ICallHandler Members

public IMethodReturn Invoke(IMethodInvocation input, GetNextHandlerDelegate getNext)
{
if (cachingService.Contains(Key))
return input.CreateMethodReturn(cachingService.Retrieve(Key));

var retVal = getNext()(input, getNext);
if (retVal.Exception == null)
cachingService.Store(Key, retVal.ReturnValue);

return retVal;
}

public string Key { get; set; }

#endregion
}

The first part of the Invoke method checks to see if there is a value in the cache with the desired key already. If there is it creates an IMethodReturn object that contains the value found in cache using a helper method provided by the input. If the value is not already in the cache it asks the next method in the chain (the real one in this case) to provide a value. If the real method didn't throw an exception then it stores the value in the cache (for next time). Pretty nifty huh? There's obviously much more to do here as the Cached attribute doesn't handle parameters (there isn't one on the get_Property method) but it is just a little sample of what we might do and it wouldn't be that hard to modify. Here's the Invoke method from the corresponding ClearsCacheCallHandler:

public IMethodReturn Invoke(IMethodInvocation input, GetNextHandlerDelegate getNext)
{
cachingService.Remove(Key);
return getNext().Invoke(input, getNext);
}

And here's another one that I've been using to check timing information. It uses an IProfilingService to store all of the profile info until a report is asked for at the end of my application. This is from the ProfileCallHandler:

public IMethodReturn Invoke(IMethodInvocation input, GetNextHandlerDelegate getNext)
{
var start = DateTime.Now;
var retVal = getNext()(input, getNext);
var end = DateTime.Now;
var duration = end - start;

string methodName = input.Target.GetType().FullName + "#" + input.MethodBase.Name;
profiler.RecordMethodExecution(methodName, duration);

return retVal;
}

Hopefully you are beginning to see the power of this mechanism. I once saw a Call Handler (implemented for Windsor) which re-tried the method call up to three times after a Timeout Exception was thrown. Caching, Logging, Exception Handling, Auditing, Security. All of these things are really infrastructure concerns that have very little to do with your business functionality and it's a good idea to separate them as much as possible.


Although I've shown the Interception mechanism working with Attributes there are a whole heap of other ways to select CallHandlers from configuration to naming and location mechanisms. There are also three different interceptors available which are the VirtualMethodInterceptor (shown above), the InterfaceInterceptor (creates a new object which has the same interface as the wrapped object) and a TransparentProxyInterceptor (which uses Remoting-Fu).


You can get more info on the Unity CodePlex site. I also found an MSDN article and a video by David Hayden. Take a look.


 

unityasp-netopen-source
Posted by: Mike Minutillo
Last revised: 27 May, 2011 02:42 PM History

Comments

Anonymous
Anonymous
21 Nov, 2009 10:29 AM @ version 0

Who knows where to download XRumer 5.0 Palladium?
Help, please. All recommend this program to effectively advertise on the Internet, this is the best program!

No new comments are allowed on this post.