PostSharp 4.3 / / Post­Sharp Documentation / Custom Patterns / Testing and Debugging Aspects / Consuming Dependencies from the Aspect / Using Contextual Dependency Resolution
Using Contextual Dependency Resolution

The dependency resolution strategy does not necessarily need to resolve to the same service implementation for all occurrences of the dependency. It is possible to design a strategy that depends on the context. For instance, the service locator could accept the aspect type and the target element of code as parameters. Test code could configure the service locator to resolve dependencies to specific implementations for a given context.

Evaluating context-sensitive rules maybe CPU-intensive, but it needs to be done only during testing. In production mode, dependency resolution can be delegated to a global service catalog.

Example: testable logging aspect with contextual dependency resolution

The following code snippet shows a logging aspect and how it could be used in production code:

C#
                        using
                         System;
                        

                        using
                         System.Collections.Generic;
                        

                        using
                         System.ComponentModel.Composition;
                        

                        using
                         System.ComponentModel.Composition.Hosting;
                        

                        using
                         System.ComponentModel.Composition.Primitives;
                        

                        using
                         System.Reflection;
                        

                        using
                         PostSharp.Aspects;
                        

                        using
                         PostSharp.Extensibility;
                        

                        using
                         PostSharp.Serialization;
                        


                        namespace
                         DependencyResolution.Contextual
{
    
                        public
                         
                        interface
                         ILogger
    {
        
                        void
                         Log(
                        string
                         message);
    }

    
                        public
                         
                        static
                         
                        class
                         AspectServiceLocator
    {
        
                        private
                         
                        static
                         CompositionContainer container;
        
                        private
                         
                        static
                         HashSet<
                        object
                        > rules = 
                        new
                         HashSet<
                        object
                        >();

        
                        public
                         
                        static
                         
                        void
                         Initialize(ComposablePartCatalog catalog)
        {
            container = 
                        new
                         CompositionContainer(catalog);
        }

        
                        public
                         
                        static
                         Lazy<T> GetService<T>(Type aspectType, MemberInfo targetElement) 
                        where
                         T : 
                        class
                        
        {
            
                        return
                         
                        new
                         Lazy<T>(() => GetServiceImpl<T>(aspectType, targetElement));
        }

        
                        private
                         
                        static
                         T GetServiceImpl<T>(Type aspectType, MemberInfo targetElement) 
                        where
                         T : 
                        class
                        
        {
            
                        // The rule implementation is naive but this is for testing purpose only.
                        

                                    
                        foreach
                         (
                        object
                         rule 
                        in
                         rules)
            {
                DependencyRule<T> typedRule = rule 
                        as
                         DependencyRule<T>;
                
                        if
                         (typedRule == 
                        null
                        ) 
                        continue
                        ;

                T service = typedRule.Rule(aspectType, targetElement);
                
                        if
                         (service != 
                        null
                        ) 
                        return
                         service;
            }

            
                        if
                         (container == 
                        null
                        )
                
                        throw
                         
                        new
                         InvalidOperationException();

            
                        // Fallback to the container, which should be the default and production behavior.
                        

                                    
                        return
                         container.GetExport<T>().Value;
        }

        
                        public
                         
                        static
                         IDisposable AddRule<T>(Func<Type, MemberInfo, T> rule)
        {
            DependencyRule<T> dependencyRule = 
                        new
                         DependencyRule<T>(rule);
            rules.Add(dependencyRule);
            
                        return
                         dependencyRule;
        }

        
                        private
                         
                        class
                         DependencyRule<T> : IDisposable
        {
            
                        public
                         DependencyRule(Func<Type, MemberInfo, T> rule)
            {
                
                        this
                        .Rule = rule;
            }

            
                        public
                         Func<Type, MemberInfo, T> Rule { 
                        get
                        ; 
                        private
                         
                        set
                        ; }

            
                        public
                         
                        void
                         Dispose()
            {
                rules.Remove(
                        this
                        );
            }
        }
    }

    [PSerializable]
    
                        public
                         
                        class
                         LogAspect : OnMethodBoundaryAspect
    {
        
                        private
                         Lazy<ILogger> logger;


        
                        public
                         
                        override
                         
                        void
                         RuntimeInitialize(MethodBase method)
        {
            logger = AspectServiceLocator.GetService<ILogger>(
                        this
                        .GetType(), method);
        }


        
                        public
                         
                        override
                         
                        void
                         OnEntry(MethodExecutionArgs args)
        {
            logger.Value.Log(
                        "OnEntry"
                        );
        }
    }

    
                        internal
                         
                        class
                         Program
    {
        
                        private
                         
                        static
                         
                        void
                         Main(
                        string
                        [] args)
        {
            AspectServiceLocator.Initialize(
                        new
                         TypeCatalog(
                        typeof
                         (ConsoleLogger)));

            LoggedMethod();
        }

        [LogAspect]
        
                        public
                         
                        static
                         
                        void
                         LoggedMethod()
        {
            Console.WriteLine(
                        "Hello, world."
                        );
        }
    }


    [Export(
                        typeof
                         (ILogger))]
    
                        internal
                         
                        class
                         ConsoleLogger : ILogger
    {
        
                        public
                         
                        void
                         Log(
                        string
                         message)
        {
            Console.WriteLine(message);
        }
    }
}
                      

The following code snippet shows how the logging aspect can be tested:

C#
                        using
                         System;
                        

                        using
                         System.Text;
                        

                        using
                         Microsoft.VisualStudio.TestTools.UnitTesting;
                        


                        namespace
                         DependencyResolution.Contextual.Test
{
    [TestClass]
    
                        public
                         
                        class
                         TestLogAspect
    {
        [TestMethod]
        
                        public
                         
                        void
                         TestMethod()
        {
            
                        // The ServiceLocator can be initialized for each test.
                        

                                    
                        using
                         (
                AspectServiceLocator.AddRule<ILogger>(
                    (type, member) =>
                    type == 
                        typeof
                         (LogAspect) && member.Name == 
                        "TargetMethod"
                         ? 
                        new
                         TestLogger() : 
                        null
                        )
                )
            {
                TestLogger.Clear();
                TargetMethod();
                Assert.AreEqual(
                        "OnEntry"
                         + Environment.NewLine, TestLogger.GetLog());
            }
        }

        [LogAspect]
        
                        public
                         
                        void
                         TargetMethod()
        {
        }
    }

    
                        internal
                         
                        class
                         TestLogger : ILogger
    {
        
                        public
                         
                        static
                         
                        readonly
                         StringBuilder stringBuilder = 
                        new
                         StringBuilder();

        
                        public
                         
                        void
                         Log(
                        string
                         message)
        {
            stringBuilder.AppendLine(message);
        }

        
                        public
                         
                        static
                         
                        string
                         GetLog()
        {
            
                        return
                         stringBuilder.ToString();
        }

        
                        public
                         
                        static
                         
                        void
                         Clear()
        {
            stringBuilder.Clear();
        }
    }
}