This post continues on from the previous post. Part 3 Enterprise Library 6, Unity 3 and MVC 4, Registration by Convention Part 3
Microsoft.Practices.Unity.Interception
Unity provides both instance and type interception, and enables interception with either objects you resolve through the container, or by using the stand-alone API to explicitly invoke interception on a known instance.
In this post we want to add a decorator class using unity interception which can be used to log the diagnosis messages. No diagnosis logs are required in the business class.
Unity Inception has 3 different types of interception:
- Transparent Proxy Interceptor
- Interface Interceptor
- Virtual Method Interceptor
We use the Interface Interceptor in this example.
Description (Taken from unity doc) An instance interceptor. It can proxy only one interface on the object. It uses dynamic code generation to create the proxy class. The InterfaceInterceptor can only be used to intercept a single interface.
Lets begin:
Get Microsoft.Practices.Unity.Interception packages from nuget:
Create a EventSource for the diagnosis:
using System.Diagnostics.Tracing; namespace MvcUnityBootstrapperTest.Logging { [EventSource(Name = "DiagnosisEvents")] public class DiagnosisEvents : EventSource { public static readonly DiagnosisEvents Log = new DiagnosisEvents(); private const int methodEnterEventId = 1; private const int methodLeaveEventId = 2; private const int logVerboseMessageEventId = 3; private const int logInfoMessageEventId = 4; [Event(methodEnterEventId, Message = "Method Enter: {0}", Level = EventLevel.Verbose)] public void MethodEnter(string message) { if (IsEnabled()) WriteEvent(methodEnterEventId, message); } [Event(methodLeaveEventId, Message = "Method Leave: {0}", Level = EventLevel.Verbose)] public void MethodLeave(string message) { if (IsEnabled()) WriteEvent(methodLeaveEventId, message); } [Event(logVerboseMessageEventId, Message = "{0}", Level = EventLevel.Verbose)] public void LogVerboseMessage(string message) { if (IsEnabled()) WriteEvent(logVerboseMessageEventId, message); } [Event(logInfoMessageEventId, Message = "{0}", Level = EventLevel.Informational)] public void LogInfoMessage(string message) { if (IsEnabled()) WriteEvent(logInfoMessageEventId, message); } } }
Now this event source can be used in the DiagnosisBehaviour : IInterceptionBehavior class
using System; using System.Collections.Generic; using Microsoft.Practices.Unity.InterceptionExtension; using MvcUnityBootstrapperTest.Logging; namespace MvcUnityBootstrapperTest.UnityExtensions { public class DiagnosisBehaviour : IInterceptionBehavior { public IEnumerable<Type> GetRequiredInterfaces() { return Type.EmptyTypes; } public IMethodReturn Invoke(IMethodInvocation input, GetNextInterceptionBehaviorDelegate getNext) { DiagnosisEvents.Log.MethodEnter(String.Format("[{0}:{1}]", this.GetType().Name, "Invoke")); DiagnosisEvents.Log.LogVerboseMessage(String.Format("{0} {1}", input.MethodBase.ToString(), input.Target.ToString())); var methodReturn = getNext().Invoke(input, getNext); if (methodReturn.Exception == null) { DiagnosisEvents.Log.MethodLeave(String.Format("Successfully finished {0} {1}", input.MethodBase.ToString(), input.Target.ToString())); } else { DiagnosisEvents.Log.MethodLeave(String.Format("Finished {0} with exception {1}: {2}", input.MethodBase.ToString(), methodReturn.Exception.GetType().Name, methodReturn.Exception.Message)); } return methodReturn; } public bool WillExecute { get { return true; } } } }
And now the InterfaceInterceptor can be added to the UnityConfig
using System; using Microsoft.Practices.Unity; using MvcUnityBootstrapperTest.UnityExtensions; using Microsoft.Practices.Unity.InterceptionExtension; namespace MvcUnityBootstrapperTest.App_Start { /// <summary> /// Specifies the Unity configuration for the main container. /// </summary> public class UnityConfig { #region Unity Container private static Lazy<IUnityContainer> container = new Lazy<IUnityContainer>(() => { var container = new UnityContainer(); RegisterTypes(container); return container; }); public static IUnityContainer GetConfiguredContainer() { return container.Value; } #endregion public static void RegisterTypes(IUnityContainer container) { container.AddNewExtension<Interception>(); container.RegisterTypes(UnityHelpers.GetTypesWithCustomAttribute<UnityIoCPerRequestLifetimeAttribute>(AppDomain.CurrentDomain.GetAssemblies()), WithMappings.FromMatchingInterface, WithName.Default, PerRequest, getInjectionMembers: t => new InjectionMember[] { new Interceptor<InterfaceInterceptor>(), new InterceptionBehavior<MvcUnityBootstrapperTest.UnityExtensions.DiagnosisBehaviour>() }) .RegisterTypes(UnityHelpers.GetTypesWithCustomAttribute<UnityIoCTransientLifetimeAttribute>(AppDomain.CurrentDomain.GetAssemblies()), WithMappings.FromMatchingInterface, WithName.Default, WithLifetime.Transient ); } public static Func<System.Type, Microsoft.Practices.Unity.LifetimeManager> PerRequest = (x) => new PerRequestLifetimeManager(); } }
Required are the code snippets as shown below.
container.AddNewExtension<Interception>(); getInjectionMembers: t => new InjectionMember[] { new Interceptor<InterfaceInterceptor>(), new InterceptionBehavior<MvcUnityBootstrapperTest.UnityExtensions.DiagnosisBehaviour>() })
Now all classes with the UnityIoCPerRequestLifetimeAttribute attribute will be intercepted using the InterfaceInterceptor
You can see from the log results that the business classes have been intercepted:
code: https://github.com/damienbod/MvcUnityInterfaceInterception.git
Part 1 Enterprise Library 6, Unity 3 with ASP.NET MVC 4
Part 2 Enterprise Library 6, Unity 3 and MVC 4, LifetimeManagers
Part 3 Enterprise Library 6, Unity 3 and MVC 4, Registration by Convention
Part 5 Enterprise Library 6, Unity 3, MVC, Validation with Interception ValidationCallHandler5
Links:
http://unity.codeplex.com/discussions/448041
http://msdn.microsoft.com/en-us/library/ff660861%28v=pandp.20%29.aspx
Thanks for your efforts. Part 2 of this series was a great help to me, but with Part 4 I get a reflection exception from the GetTypesWithCustomAttributes method with a LoaderException message of “Could not load file or assembly ‘Microsoft.Practices.ServiceLocation'”. Any suggestions? VS2012 on Win7x64.
Found it! In case someone else needs it…
PM> Install-Package CommonServiceLocator
Great thanks for the info
greetings Damien
Great series of Interception posts, very helpful. Can I get the code as downloadable project ?
Thanks, it’s here:
https://github.com/damienbod/MvcUnityInterfaceInterception
greetings Damien