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

Both previous approaches have a static dependency resolution strategy: it cannot be changed over time. Therefore, these strategies could be unsuitable in cases where several tests need different configurations of the dependency container.

A possible solution is to resolve dependencies dynamically each time they are needed, and not only at aspect initialization. Although this solution is ideal for the sake of testing, it may be too inefficient for production. Therefore, the solution would still need to provide dependency caching for production mode. Caching would neutralize the dynamic characteristics of dependency resolution.

This solution would be based on the following elements:

  1. The service locator can be initialized in two modes: production (the resolution strategy is immutable) and testing (the resolution strategy can be modified).

  2. The service locator returns a delegate (Func<T>, where T is the dependency type), instead of the dependency itself (T or Lazy<T>).

  3. The aspect calls the service locator during aspect initialization and stores the delegate.

  4. The aspect calls the delegate at runtime.

Example: testable logging aspect with a global MEF service container with dynamic resolution

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

C#
                        using
                         System;
                        

                        using
                         System.ComponentModel.Composition;
                        

                        using
                         System.ComponentModel.Composition.Hosting;
                        

                        using
                         System.ComponentModel.Composition.Primitives;
                        

                        using
                         PostSharp.Aspects;
                        

                        using
                         PostSharp.Extensibility;
                        

                        using
                         PostSharp.Serialization;
                        


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

    
                        public
                         
                        static
                         
                        class
                         AspectServiceLocator
    {
        
                        private
                         
                        static
                         CompositionContainer container;
        
                        private
                         
                        static
                         
                        bool
                         isCacheable;

        
                        public
                         
                        static
                         
                        void
                         Initialize(ComposablePartCatalog catalog, 
                        bool
                         isCacheable)
        {
            
                        if
                         (AspectServiceLocator.isCacheable && container != 
                        null
                        )
                
                        throw
                         
                        new
                         InvalidOperationException();

            container = 
                        new
                         CompositionContainer(catalog);
            AspectServiceLocator.isCacheable = isCacheable;
        }

        
                        public
                         
                        static
                         Func<T> GetService<T>() 
                        where
                         T : 
                        class
                        
        {
            
                        if
                         (isCacheable)
            {
                
                        return
                         () => 
                        new
                         Lazy<T>(GetServiceImpl<T>).Value;
            }
            
                        else
                        
            {
                
                        return
                         GetServiceImpl<T>;
            }
        }

        
                        private
                         
                        static
                         T GetServiceImpl<T>()
        {
            
                        if
                         (container == 
                        null
                        )
                
                        throw
                         
                        new
                         InvalidOperationException();

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

    [PSerializable]
    
                        public
                         
                        class
                         LogAspect : OnMethodBoundaryAspect
    {
        
                        private
                         
                        static
                         
                        readonly
                         Func<ILogger> logger;

        
                        static
                         LogAspect()
        {
            
                        if
                         (!PostSharpEnvironment.IsPostSharpRunning)
            {
                logger = AspectServiceLocator.GetService<ILogger>();
            }
        }


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

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

            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.ComponentModel.Composition;
                        

                        using
                         System.ComponentModel.Composition.Hosting;
                        

                        using
                         System.Text;
                        

                        using
                         Microsoft.VisualStudio.TestTools.UnitTesting;
                        


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

                                    AspectServiceLocator.Initialize(
                        new
                         TypeCatalog(
                        typeof
                         (TestLogger)), 
                        false
                        );

            TestLogger.Clear();
            TargetMethod();
            Assert.AreEqual(
                        "OnEntry"
                         + Environment.NewLine, TestLogger.GetLog());
        }


        [LogAspect]
        
                        private
                         
                        void
                         TargetMethod()
        {
        }
    }

    [Export(
                        typeof
                         (ILogger))]
    
                        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();
        }
    }
}