Unity – Part 4: Extensions

Extensions

Another long overdue post on Unity. See the first here for an introduction, the second here for dependency injection and the third here for AOP with Unity.

Unity allows adding extensions to it. An extension is something that enhances its functionality somehow, or that configures some aspect of it so that you don’t have to do it manually. In Unity, an extension is a class that inherits from the abstract base class UnityContainerExtension. It can have multiple extensions, which are processed by the order on which they are declared – more on this later. The collection of actually loaded extensions is hidden inside of Unity, the only operations you can perform are add or remove an extension.

You already know from post three that if you want to use AOP – or Interception, in Unity terms – you must add the Interception extension. You also saw that we must tell Unity which of the interception strategies it must use for each registration upon which you want to apply aspects – VirtualMethodInterceptor, TransparentProxyInterceptor or InterfaceInterceptor. This is rather boring, so why not turn this into an extension that does all the work? The following code does just that:

   1: public class DefaultInterception : Interception
   2: {
   3:     #region Private static readonly fields
   4:     private static readonly IInterceptor [] interceptors = typeof(IInterceptor)
   5:         .Assembly
   6:         .GetExportedTypes()
   7:         .Where(type => 
   8:             (typeof(IInterceptor).IsAssignableFrom(type) == true) &&
   9:             (type.IsAbstract == false) &&
  10:             (type.IsInterface == false))
  11:         .Select(type => Activator.CreateInstance(type) as IInterceptor)
  12:         .ToArray();
  13:     #endregion
  14:  
  15:     #region Protected override methods
  16:     protected override void Initialize()
  17:     {
  18:         base.Initialize();
  19:  
  20:         IConfigurationSource configSource = ConfigurationSourceFactory.Create();
  21:         PolicyInjectionSettings section = configSource.GetSection(PolicyInjectionSettings.SectionName) as PolicyInjectionSettings;
  22:  
  23:         if (section != null)
  24:         {
  25:             section.ConfigureContainer(this.Container, configSource);
  26:         }
  27:  
  28:         this.Context.Registering += delegate(Object sender, RegisterEventArgs e)
  29:         {                
  30:             this.setInterceptorFor(e.TypeFrom, e.TypeTo, e.Name, e.LifetimeManager);
  31:         };
  32:  
  33:         this.Context.RegisteringInstance += delegate(Object sender, RegisterInstanceEventArgs e)
  34:         {
  35:             this.setInstanceInterceptorFor(e.RegisteredType, e.Name, e.Instance, e.LifetimeManager);
  36:         };
  37:     }
  38:     #endregion
  39:  
  40:     #region Private methods
  41:     private void setInterceptorFor(Type typeFrom, Type typeTo, String name, LifetimeManager lifetimeManager)
  42:     {
  43:         foreach (IInterceptor interceptor in interceptors)
  44:         {
  45:             if ((interceptor.CanIntercept(typeFrom) == true) && (interceptor.GetInterceptableMethods(typeFrom, typeTo).Count() != 0))
  46:             {
  47:                 if (interceptor is IInstanceInterceptor)
  48:                 {                        
  49:                     this.Container.Configure<Interception>().SetInterceptorFor(typeFrom, name, interceptor as IInstanceInterceptor);
  50:                 }
  51:                 else if (interceptor is ITypeInterceptor)
  52:                 {
  53:                     this.Container.Configure<Interception>().SetInterceptorFor(typeFrom, name, interceptor as ITypeInterceptor);
  54:                 }
  55:  
  56:                 //add a custom behavior for all types
  57:  
  58:                 break;
  59:             }
  60:         }
  61:     }
  62:  
  63:     private void setInstanceInterceptorFor(Type registeredType, String name, Object instance, LifetimeManager manager)
  64:     {
  65:         foreach (IInstanceInterceptor interceptor in interceptors.OfType<IInstanceInterceptor>())
  66:         {
  67:             if ((interceptor.CanIntercept(registeredType) == true) && (interceptor.GetInterceptableMethods(registeredType, instance.GetType()).Count() != 0))
  68:             {
  69:                 this.Container.Configure<Interception>().SetInterceptorFor(registeredType, name, interceptor);
  70:                 break;
  71:             }
  72:         }
  73:     }
  74:     #endregion
  75: }

As you can see, I inherited from Interception, which provides the AOP functionality, so I don't have to add it too. On the Initialize method we have access to both the Container (IUnityContainer) as well as a Context (ExtensionObject), which we can access freely. In this case, I hooked up to its Registering and RegisteringInstance events and, whenever a registration is made, I set up an appropriate interceptor, which I got from the list of default interceptor (IInterceptor) implementations.

Registration By Code

So, you need to register this extension, which you can do by code, by using the AddExtension or AddNewExtension<T> methods:

   1: IUnityContainer unity = ...;
   2:  
   3: unity.AddNewExtension<DefaultInterception>();

Beware, you must do this before actually registering something, because if you do it later, the events won’t get fired.

Registration By Configuration

If you place the registration on the .config file, you do no have to be concerned about doing it before the registrations:

   1: <configuration>
   2:     <configSections>
   3:         <section name="unity" type="Microsoft.Practices.Unity.Configuration.UnityConfigurationSection, Microsoft.Practices.Unity.Configuration"/>
   4:     </configSections>
   5:     <unity xmlns="http://schemas.microsoft.com/practices/2010/unity">
   6:         <container>
   7:             <extension type="MyNamespace.DefaultInterception, MyAssembly"/>
   8:         </container>
   9:     </unity>
  10: </configuration>

Configuring

In both cases, if the extension needs configuring, you can access it by using the Configure method:

   1: DefaultInterception extension = unity.Configure<DefaultInterception>();

This will only return the already registered extension, not create a new one.

Other Uses

Other common uses might include, for example, setting up the Common Service Locator, or setting up some interception behavior.

Next in line: injecting values into registrations. Stay tuned!

                             

No Comments