Custom Dependency Injection Wrapper

Introduction

Over two years, I have been using different dependency injection frameworks. As I changed frameworks, I had to design my wrapper to encapsulate the framework.

So I started with a simple resolver interface. I use this to implement dependency injection frameworks such as Unity, Structure Map, Spring, etc…

namespace Framework.Containers
{
    public interface IDependencyResolver
    {
        T Resolve<T>();
        T Resolve<T>(string name);
        IEnumerable<T> ResolveAll<T>();
        void Clear();
    }
}

There are two ways to build a container from:

  1. Configuration based – XML file which maps interfaces to concrete types with lifetime settings and parameters.
  2. Dynamic – Add types and their interfaces to the container.

Configuration based

To load configuration settings to the container, we need a config based resolver.

namespace Framework.Containers
{
    public interface IConfigBasedDependencyResolver : IDependencyResolver
    {
        void LoadDependencies();
    }
}

Dynamic

Containers can also be built by adding interfaces and concrete implementation dynamically without depending on config files.

namespace Framework.Containers
{
    public interface IDynamicDependencyResolver : IDependencyResolver
    {
        void Register<T>(object obj);
        void Register<T>(object obj, string name);
        void RegisterType(Type type, params object[] parameters);
    }
}

Implementation

Base dependency resolver base class keeps a list of types and the IDepenencyResolver contract implementations.

namespace Framework.Containers
{
    public abstract class DependencyResolverBase : IDependencyResolver
    {
        protected IDictionary<Type, object> m_singletons = new Dictionary<Type, object>();
        protected IDictionary<string, object> m_namedSingletons = new Dictionary<string, object>();
        protected IDictionary<string, Type> m_types = new Dictionary<string, Type>();
        protected IDictionary<Type, object[]> m_typeParams = new Dictionary<Type, object[]>();

        public T Resolve<T>()
        {
            var requestedType = typeof(T);
            var type = m_types.Values.FirstOrDefault(requestedType.IsAssignableFrom);

            return (T)CreateInstance(type);
        }

        public T Resolve<T>(string name)
        {
            var instance = default(T);

            if (m_types.ContainsKey(name))
            {
                if (m_namedSingletons.ContainsKey(name))
                {
                    instance = (T)m_namedSingletons[name];
                }
                else
                {
                    var type = m_types[name];
                    instance = (T)CreateInstance(type);
                }
            }

            return instance;
        }

        public IEnumerable<T> ResolveAll<T>()
        {
            var requestedType = typeof(T);

            return (from potentialType in m_types.Values
                    where requestedType.IsAssignableFrom(potentialType)
                    select (T) CreateInstance(potentialType)).ToList();
        }

        public void Clear()
        {
            m_singletons = new Dictionary<Type, object>();
            m_namedSingletons = new Dictionary<string, object>();
            m_types = new Dictionary<string, Type>();
            m_typeParams = new Dictionary<Type, object[]>();
        }

        private object CreateInstance(Type type)
        {
            object instance = null;
            object[] parameters = null;

            if (m_typeParams.ContainsKey(type))
                parameters = m_typeParams[type];

            if (type != null && parameters != null)
            {
                if (m_types.ContainsKey(type.FullName) && m_typeParams.ContainsKey(type))
                {
                    instance = Activator.CreateInstance(type, parameters);
                }
            }
            else if (type != null)
            {
                instance = m_singletons.ContainsKey(type) ? m_singletons[type] : Activator.CreateInstance(type);
            }

            return instance;
        }
    }
}

Config based dependency resolver implementation loads types and interfaces from configuration file.

namespace Framework.Containers
{
    public class ConfigBasedDependencyResolver : DependencyResolverBase, IConfigBasedDependencyResolver
    {
        public void LoadDependencies()
        {
            var config = (DependencyConfiguration)ConfigurationManager
                                                      .GetSection(DependencyConfigurationSectionHandler.SECTION_NAME);

            m_types = new Dictionary<string, Type>();

            foreach (var dependency in config.Elements)
            {
                var type = Type.GetType(dependency.TypeName);
                m_types.Add(dependency.Name, type);

                if (dependency.Singleton)
                {
                    object instance = Activator.CreateInstance(type);
                    m_singletons.Add(type, instance);
                    m_namedSingletons.Add(dependency.Name, instance);
                }
            }
        }
    }
}

The dynamic dependency resolver implementation is straight forward..

namespace Framework.Containers
{
    public class DynamicDependencyResolver : DependencyResolverBase, IDynamicDependencyResolver
    {
        public void Register<T>(object obj)
        {
            if (obj is T == false)
            {
                throw new InvalidOperationException(
                    string.Format("The supplied instance {0} does not implement {1}",
                                  obj.GetType().FullName,
                                  typeof(T).FullName));
            }

            m_types.Add(typeof(T).FullName, obj.GetType());
        }

        public void Register<T>(object obj, string name)
        {
            if (obj is T == false)
            {
                throw new InvalidOperationException(
                    string.Format("The supplied instance {0} does not implement {1}",
                                  obj.GetType().FullName,
                                  typeof(T).FullName));
            }

            m_types.Add(name, obj.GetType());
        }

        public void RegisterType(Type type, params object[] parameters)
        {
            if (type == null)
                throw new InvalidOperationException("The supplied type is null");

            if (parameters != null)
                m_typeParams.Add(type, parameters);

            m_types.Add(type.FullName, type);
        }
    }
}

And finally the facade to the container…

namespace Framework.Containers
{
    public static class Dependency
    {
        public static IDependencyResolver Container { get; private set; }

        public static void Initialize(IDependencyResolver resolver)
        {
            Container = resolver;
        }

        public static T Resolve<T>(string name)
        {
            return Container.Resolve<T>(name);
        }

        public static T Resolve<T>()
        {
            return Container.Resolve<T>();
        }

        public static IEnumerable<T> ResolveAll<T>()
        {
            return Container.ResolveAll<T>();
        }

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

If you have noticed, the Container property exposes the IDependencyResolver as read only. If you used a config based dependency resolver and then wanted to add types to container dynamically, you can cast it to IDynamicDependencyResolver and register new types…

Usage with existing frameworks

The whole point of this wrapper is to use it with other dependency injection frameworks. You can implement IDependencyResolver and/or IDynamicDependencyResolver on a concrete class and initialize the Dependency with Dependency.Initialize(new MyUnityContainerImplementation()).

I hope this helps…