Wednesday, 23 January 2013

Configuring Unity container at runtime

 
1. Some business logic first
 
    public interface ILogger
    {
        void Log(string information);
    }
    
    public class Logger : ILogger
    {
        public void Log(string information)
        {
            Console.WriteLine(information);
        }
    }
 
    public class Employee
    {
        public string FirstName { getset; }
 
        public string LastName { getset; }
 
        public DateTime DOB { getset; }
 
        public Guid ID { getset; }
    }
 
    public interface IEmployeeService
    {
        [BeforeHandler]
        [AfterHandler1]
        [AfterHandler2]        
        bool AddOrUpdate(Employee employee);
 
        [BeforeHandler]
        [AfterHandler1]
        [AfterHandler2]
        Employee Get(Guid id); 
 
        [BeforeHandler]
        [AfterHandler1]
        [AfterHandler2]          
        bool Delete(Guid id);
    }
 
    public class EmployeeService : IEmployeeService
    {
        public EmployeeService(ILogger logger)
        {
            this.Logger = logger;
        }
 
        private ILogger Logger { getset; }
        #region IEmployeeService Members
 
        public bool AddOrUpdate(Employee employee)
        {
            this.Logger.Log("AddOrUpdate called");
 
            return true;
        }
 
        public Employee Get(Guid id)
        {
            this.Logger.Log("Get called");
 
            return new Employee();
        }
 
        public bool Delete(Guid id)
        {
            this.Logger.Log("Delete called");
 
            return true;
        }
 
        #endregion
    }
 
2. Now Add some handlers/Matching rules 

public class AlwaysMatchingRule : IMatchingRule
    {
        public bool Matches(System.Reflection.MethodBase member)
        {
            return true;
        }
    }
 
    public class BeforeHandler : ICallHandler
    {
        public IMethodReturn Invoke(IMethodInvocation input, GetNextHandlerDelegate getNext)
        {
            Console.WriteLine("CallHandler '{0}' will now execute '{1}', Order '{2} "this.GetType().Name, input.MethodBase.Name, this.Order);
 
            return getNext().Invoke(input, getNext);
        }
 
        public int Order { getset; }
    }
 
    public class AfterHandler1 : ICallHandler
    {
        public IMethodReturn Invoke(IMethodInvocation input, GetNextHandlerDelegate getNext)
        {
            var result =  getNext().Invoke(input, getNext);
 
            Console.WriteLine("CallHandler '{0}' executed '{1}', Order '{2}' "this.GetType().Name, input.MethodBase.Name, this.Order);
 
            return result;
        }
 
        public int Order { getset; }
    }
 
    public class AfterHandler2 : ICallHandler
    {
        public IMethodReturn Invoke(IMethodInvocation input, GetNextHandlerDelegate getNext)
        {
            var result = getNext().Invoke(input, getNext);
 
            Console.WriteLine("CallHandler '{0}' executed '{1}', Order '{2}' "this.GetType().Name, input.MethodBase.Name, this.Order);
 
            return result;
        }
 
        public int Order { getset; }
    }
 
    public class BeforeHandlerAttribute : HandlerAttribute
    {
        public override ICallHandler CreateHandler(Microsoft.Practices.Unity.IUnityContainer container)
        {
            return new BeforeHandler();
        }
    }
 
    public class AfterHandler1Attribute : HandlerAttribute
    {
        public override ICallHandler CreateHandler(Microsoft.Practices.Unity.IUnityContainer container)
        {
            return new AfterHandler1();
        }
    }
 
    public class AfterHandler2Attribute : HandlerAttribute
    {
        public override ICallHandler CreateHandler(Microsoft.Practices.Unity.IUnityContainer container)
        {
            return new AfterHandler2();
        }
    } 
 
3. Now configuring container 

        static void Main(string[] args)
        {
            //case 1: simple configuration
            IUnityContainer container1 = new UnityContainer()
                                        .RegisterType<ILogger, Logger>()
                                        .RegisterType<IEmployeeService, EmployeeService>(new InjectionConstructor(typeof(ILogger)));

            //case 2: Here ILogger will be resolved automatically, not need to inject
            IUnityContainer container2 = new UnityContainer()
                                        .RegisterType<ILogger, Logger>()
                                        .RegisterType<IEmployeeService, EmployeeService>();

            //case 3: Different way(obselete) of injection
            IUnityContainer container3 = new UnityContainer()
                                        .RegisterType<ILogger, Logger>()
                                        .RegisterType<IEmployeeService, EmployeeService>()
                                        .Configure<InjectedMembers>()
                                        .ConfigureInjectionFor<EmployeeService>(
                                        new InjectionConstructor(typeof(ILogger))).Container;

            //case 4: Configuring with Policies->Rules->CallHandlers and 
            IUnityContainer container4 = new UnityContainer();

            container4.AddNewExtension<Interception>();
            
            container4
                        .Configure<Interception>()
                            .AddPolicy("Trace")
                                .AddMatchingRule("AlwaysMatchingRule")
                                .AddCallHandler("BeforeHandler")
                                .AddCallHandler("AfterHandler1")
                                .AddCallHandler("AfterHandler2").Interception.Container
                        .RegisterType<IMatchingRule, AlwaysMatchingRule>("AlwaysMatchingRule")
                        .RegisterType<ICallHandler, BeforeHandler>("BeforeHandler", new InjectionProperty("Order", 1))
                        .RegisterType<ICallHandler, AfterHandler1>("AfterHandler1", new InjectionProperty("Order", 2))
                        .RegisterType<ICallHandler, AfterHandler2>("AfterHandler2", new InjectionProperty("Order", 3))
                        .RegisterType<ILogger, Logger>()
                        .RegisterType<IEmployeeService, EmployeeService>()
                        .Configure<Interception>()
                        .SetInterceptorFor<IEmployeeService>(new TransparentProxyInterceptor());

            //case 5: Using handler attributes - simplest of configuration
            IUnityContainer container5 = new UnityContainer();

            container5.AddNewExtension<Interception>();
            container5
                        .Configure<Interception>()
                            .SetDefaultInterceptorFor<IEmployeeService>(new TransparentProxyInterceptor())
                        .Container
                        .RegisterType<ILogger, Logger>()
                        .RegisterType<IEmployeeService, EmployeeService>();

            ExerciseAllServices(container1.Resolve<IEmployeeService>());
            ExerciseAllServices(container2.Resolve<IEmployeeService>());
            ExerciseAllServices(container3.Resolve<IEmployeeService>());
            ExerciseAllServices(container4.Resolve<IEmployeeService>());
            ExerciseAllServices(container5.Resolve<IEmployeeService>());
 
            container1.Dispose();
            container2.Dispose();
            container3.Dispose();
            container4.Dispose();
        }

        private static void ExerciseAllServices(IEmployeeService service)
        {
            service.AddOrUpdate(null);
            service.Get(Guid.Empty);
            service.Delete(Guid.Empty);
            Console.WriteLine("-----------------------------------------------");
        } 
 
 
Some good References
http://blogsprajeesh.blogspot.in/2009/12/unity-application-block-interceptor.html 
http://msdn.microsoft.com/en-us/library/ff660871%28v=pandp.20%29.aspx 
http://msdn.microsoft.com/en-us/library/dd203208.aspx 
http://msdn.microsoft.com/en-us/library/ff647107.aspx