Geeks With Blogs

The Life and Times of a Dev Yes, we're really that weird

Unity is a great IoC container.  One thing that is lacking, however, is the ability to automatically resolve requests without some kind of mapping.  In the past, I’ve applied attributes to classes to do that mapping or used a config file.  Recently, though, I looked into extending Unity itself and discovered that it’s actually quite doable to do so. 

I’ve created an “AutoResolver Strategy” that will allow Unity to take any interface request and automagically translate that into a resolution.

It assumes that you’re using the following naming pattern for your interfaces:  “I” + Concrete Class Name.  For example, the interface for the ConfigurationService would be IConfigurationService.  A request that looked like UnityContainer.Resolve<IConfigurationService> would automatically resolve and create an instance of ConfigurationService.

To support testing, when I write code, every concrete instance has an interface, and I use dependency injection, usually constructor injection, to inject those types into the classes where they are needed.  In most cases, there’s a one to one mapping in the production code between the interface and the concrete type.  When writing unit tests, however, I’ll often need to mock the dependencies.  I prefer Moq for this.  I’ll make my mock, and then put that mock into the container that will be used to run the test with a container controlled lifetime and voila, my type has been replaced.

Basically, this gives me the best of both worlds.  I can still create a mapping or use a config file, or I can autoresolve and not mess with mappings.

Here’s the class.  Details on attaching it to the container are below.

// -----------------------------------------------------------------------
//  <copyright file="AutoResolverStrategy.cs" company="Veracity Solutions, Inc.">
//      Copyright (c) Veracity Solutions, Inc. 2011.  This code is licensed under the Microsoft Public License (MS-PL).  http://www.opensource.org/licenses/MS-PL.
//  </copyright>
//  <summary>
//      Created By: Robert J. May
//  </summary>
// -----------------------------------------------------------------------

namespace Veracity.Utilities.IoC
{
    #region Usings

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Reflection;

    using Microsoft.Practices.ObjectBuilder2;

    #endregion

    /// <summary>
    ///   Represents a strategy for performing mappings when the given type isn't already mapped.
    /// </summary>
    public class AutoResolverStrategy : BuilderStrategy
    {
        #region Constants and Fields

        /// <summary>
        ///   An object to use for locking.
        /// </summary>
        private static readonly object lockObject = new object();

        /// <summary>
        ///   A cache of type mappings that we've looked up already.
        /// </summary>
        private static readonly Dictionary<string, Type> typeCache = new Dictionary<string, Type>();

        #endregion

        #region Public Methods

        /// <summary>
        ///   Looks for an existing mapping. if not found, attempts a mapping based on the name of the resolve request.
        /// </summary>
        /// <param name="context"> The current context for the buildup. </param>
        public override void PreBuildUp(IBuilderContext context)
        {
            // Note that this is the same functionality as the default BuildKeyMappingPolicy.  The difference is that if we have no policy, we infer one.
            var policy = context.Policies.Get<IBuildKeyMappingPolicy>(context.BuildKey);

            if (policy != null)
            {
                context.BuildKey = policy.Map(context.BuildKey, context);
            }
            else
            {
                if (context.BuildKey.Type.IsInterface)
                {
                    string interFaceName = context.BuildKey.Type.Name;
                    string interfaceNamespace = context.BuildKey.Type.Namespace;

                    // By convention, all of our interfaces start with I, and by convention, the concrete type that they implement is the same name without the I.
                    if (interFaceName.StartsWith("I"))
                    {
                        string newName = interFaceName.Substring(1, interFaceName.Length - 1);
                        string fullName = interfaceNamespace + "." + newName;

                        Type concreteType = null;

                        if (typeCache.ContainsKey(fullName))
                        {
                            concreteType = typeCache[fullName];
                        }
                        else
                        {
                            Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies();

                            foreach (Assembly assembly in assemblies)
                            {
                                concreteType = assembly.GetType(fullName);
                                if (concreteType != null)
                                {
                                    lock (lockObject)
                                    {
                                        if (!typeCache.ContainsKey(fullName))
                                        {
                                            typeCache.Add(fullName, concreteType);
                                        }
                                    }

                                    break;
                                }
                            }
                        }

                        if (concreteType != null)
                        {
                            NamedTypeBuildKey oldKey = context.BuildKey;
                            NamedTypeBuildKey newBuildKey = new NamedTypeBuildKey(concreteType, null);
                            context.BuildKey = newBuildKey;

                            // look for persistant policies for this interface name
                            ILifetimePolicy lifetime = context.PersistentPolicies.Get<ILifetimePolicy>(oldKey);
                            if (lifetime != null)
                            {
                                context.PersistentPolicies.Set(lifetime, newBuildKey);
                            }
                        }
                    }
                }
            }
        }

        #endregion
    }
}

 

Once you’ve got this class into place, you’ll need to create an extension that tells Unity when to use the strategy.  I use this extension:

 

// -----------------------------------------------------------------------
//  <copyright file="AutoResolverUnityExtension.cs" company="Veracity Solutions, Inc.">
//      Copyright (c) Veracity Solutions, Inc. 2011.  This code is licensed under the Microsoft Public License (MS-PL).  http://www.opensource.org/licenses/MS-PL.
//  </copyright>
//  <summary>
//      Created By: Robert J. May
//  </summary>
// -----------------------------------------------------------------------

namespace Veracity.Utilities.IoC
{
    #region Usings

    using Microsoft.Practices.Unity;
    using Microsoft.Practices.Unity.ObjectBuilder;

    #endregion

    /// <summary>
    ///   Extends unity to automatically resolve most items based on naming convention.
    /// </summary>
    public class AutoResolverUnityExtension : UnityContainerExtension
    {
        #region Methods

        /// <summary>
        ///   Initializes this extension.
        /// </summary>
        protected override void Initialize()
        {
            this.Context.Strategies.AddNew<AutoResolverStrategy>(UnityBuildStage.TypeMapping);
        }

        #endregion
    }
}

Then, you’ll need to attach the extension to the container, like this:

var container = new UnityContainer();
container.AddNewExtension<AutoResolverUnityExtension>();

That’s it.  Enjoy!

Technorati Tags:
Posted on Wednesday, December 28, 2011 11:13 AM | Back to top


Comments on this post: Extending Unity

No comments posted yet.
Your comment:
 (will show your gravatar)


Copyright © Robert May | Powered by: GeeksWithBlogs.net