Pages

Monday 7 May 2012

Interception and ServiceLocator in Unity

This time I would like to familiarize you with two interesting concepts of Unity (IoC Container) which you can find very useful.

Interceptors allow programmers to run some functionality before or after a selected piece of code. This approach is called Aspect-Oriented Programming (AOP) and its main goal is to increase modularity through separation of cross-cutting concerns e.g. logging messages before and after a code execution. The following example shows how easy you can apply logging in a method using a [MethodHandler()] attribute. 

ServiceLocator is an excellent concept of the Unity but I do not like the fact that it can be easily abused and overdosed. An example below shows two different approaches for creating an instance of a Logger class.  First one is using constructor injection and the second one is using ServiceLocator (have a look at the constructors). The only one case scenario when ServiceLocator seems to be very useful is a situation when we have a class created outside of the Unity and this class needs access to the other types registered in a container. An example below shows how to make ServiceLocator aware of the Unity.

   1:          class Program
   2:          {
   3:              static void Main(string[] args)
   4:              {
   5:                  IUnityContainer unity = new UnityContainer();
   6:                  UnityServiceLocator locator = new UnityServiceLocator(unity);
   7:                  ServiceLocator.SetLocatorProvider(() => locator);
   8:   
   9:                  unity.AddNewExtension<Interception>();
  10:                  unity.RegisterType<ILogger, Logger>();
  11:                  unity.RegisterType<ITest, Test>().Configure<Interception>().SetDefaultInterceptorFor<ITest>(new InterfaceInterceptor());
  12:   
  13:                  unity.Resolve<ITest>().Execute();
  14:                  Console.ReadKey();
  15:              }
  16:          }
  17:   
  18:          public class Test : ITest
  19:          {
  20:              public void Execute()
  21:              {
  22:                  Console.WriteLine("Execute Method Body");
  23:              }
  24:          }
  25:   
  26:          public interface ITest
  27:          {
  28:              [MethodHandler()]
  29:              void Execute();
  30:          }
  31:   
  32:          public class CallHandler : ICallHandler
  33:          {
  34:              private IUnityContainer _container;
  35:              private ILogger _logger;
  36:   
  37:              public CallHandler(IUnityContainer container, ILogger logger)
  38:              {
  39:                  _logger = logger;
  40:              }
  41:   
  42:              public CallHandler()
  43:              {
  44:                  _logger = ServiceLocator.Current.GetInstance<ILogger>();
  45:              }
  46:   
  47:              public IMethodReturn Invoke(IMethodInvocation input, GetNextHandlerDelegate getNext)
  48:              {
  49:                  _logger.LogPreMessage(input.MethodBase.Name);
  50:                  IMethodReturn methodReturn = getNext()(input, getNext);
  51:                  _logger.LogPostMessage(input.MethodBase.Name);
  52:                  return methodReturn;
  53:              }
  54:   
  55:              public int Order
  56:              {
  57:                  get;
  58:                  set;
  59:              }
  60:          }
  61:   
  62:          public class MethodHandlerAttribute : HandlerAttribute
  63:          {
  64:              public MethodHandlerAttribute()
  65:              {
  66:              }
  67:   
  68:              public override ICallHandler CreateHandler(IUnityContainer container)
  69:              {
  70:                  return new CallHandler(container);
  71:              }
  72:          }
  73:   
  74:          interface ILogger
  75:          {
  76:              void LogPreMessage(string msg);
  77:              void LogPostMessage(string msg);
  78:          }
  79:   
  80:          class Logger : ILogger
  81:          {
  82:              public void LogPreMessage(string msg)
  83:              {
  84:                  Console.WriteLine("Pre -> " + msg);
  85:              }
  86:   
  87:              public void LogPostMessage(string msg)
  88:              {
  89:                  Console.WriteLine("Post -> " + msg);
  90:              }
  91:          }
  92:      }

Useful links:

1 comment:

  1. Unity is great - so long as it's used properly! This looks like a really good way to use Interception.

    Conversely, working in a very different environment, I'm finding myself leveraging CompilerServices to dynamically build and compile wrapper classes at runtime that log Pre and Post messages around each call, this enabling diagnostic logging including method call timings without having to rebuild the app to strip out the non-performant debug code. Something else you might be interested in is the .Net 4 CallerMemberNameAttribute and its siblings. (If you're wondering what I use instead of Unity, I've got my own dead simple IoC container.)

    ReplyDelete