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 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 (c) Veracity Solutions, Inc. 2011. This code is licensed under the Microsoft Public License (MS-PL). http://www.opensource.org/licenses/MS-PL. // //
namespace Veracity.Utilities.IoC { #region Usings
using System; using System.Collections.Generic; using System.Linq; using System.Reflection;
using Microsoft.Practices.ObjectBuilder2;
#endregion
///
///
///
#endregion
#region Public Methods
///
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 (c) Veracity Solutions, Inc. 2011. This code is licensed under the Microsoft Public License (MS-PL). http://www.opensource.org/licenses/MS-PL. // //
namespace Veracity.Utilities.IoC { #region Usings
using Microsoft.Practices.Unity; using Microsoft.Practices.Unity.ObjectBuilder;
#endregion
///
///
#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:
