Register multiple instances without naming them

The wishlist for Unity vNext contains an item with the following description:

Register multiple instances without naming them and then resolve all these instances by ResolveAll

Well that does not sound that difficult does it? You just need to mess a little with Unity’s type mapping system.

Lets write a test first that will show we got it right:

[TestMethod]
public void CanResolveMultipeDefaultMappingsUsingResolveAll()
{
  var container = new UnityContainer().AddNewExtension<Remember>();
  container.RegisterType<IFoo, One>();
  container.RegisterType<IFoo, Two>();
  container.RegisterType<IFoo, Three>();
  IFoo[] foos = container.ResolveAll<IFoo>().OrderBy(f => f.GetType().Name).ToArray();
  Assert.AreEqual(3, foos.Length);
  Assert.IsInstanceOfType(foos[0], typeof(One));
  Assert.IsInstanceOfType(foos[1], typeof(Three));
  Assert.IsInstanceOfType(foos[2], typeof(Two));
}

Armed with the Unity source code I started digging for the truth.

The call to ResolveAll uses an internal class called NamedTypeRegistry to find out which mappings where registered as named mappings. You can’t access this class directly but the ExtensionContext that is provided to every class derived from UnityContainerExtension can add mappings to the registry. The code for the Remember extension does just that. Add a mapping to the registry and add another BuildKeyMappingPolicy that maps the named mapping to one of the default mappings.

public class Remember : UnityContainerExtension
{
  protected override void Initialize()
  {
    this.Context.Registering += this.OnRegistering;
    this.Context.RegisteringInstance += this.OnRegisteringInstance;
  }
  private void OnRegisteringInstance(object sender, RegisterInstanceEventArgs e)
  {
    if(string.IsNullOrEmpty(e.Name))
    {
      string uniqueName = Guid.NewGuid().ToString();
      this.Context.RegisterNamedType(e.RegisteredType, uniqueName);
      this.Context.Policies.Set<IBuildKeyMappingPolicy>(
        new BuildKeyMappingPolicy(new NamedTypeBuildKey(e.RegisteredType)),
        new NamedTypeBuildKey(e.RegisteredType, uniqueName));
    }
  }
  private void OnRegistering(object sender, RegisterEventArgs e)
  {
    if (e.TypeFrom != null && string.IsNullOrEmpty(e.Name))
    {
      string uniqueName = Guid.NewGuid().ToString();
      this.Context.RegisterNamedType(e.TypeFrom, uniqueName);
      if (e.TypeFrom.IsGenericTypeDefinition && e.TypeTo.IsGenericTypeDefinition)
      {
        this.Context.Policies.Set<IBuildKeyMappingPolicy>(
          new GenericTypeBuildKeyMappingPolicy(new NamedTypeBuildKey(e.TypeTo)),
          new NamedTypeBuildKey(e.TypeFrom, uniqueName));
      }
      else
      {
        this.Context.Policies.Set<IBuildKeyMappingPolicy>(
          new BuildKeyMappingPolicy(new NamedTypeBuildKey(e.TypeTo)),
          new NamedTypeBuildKey(e.TypeFrom, uniqueName));
      }
    }
  }
}

And you are done. The associated test project assures that lifetimes and InjectionMembers are also respected as well as registered instances are reused. As a nice side effect a call to ResolveAll will now return all instances of all mappings including the default mapping which is otherwise omitted by Unity.

Get the source code for the Remember extension  here (project TecX.Unity folder Mapping and the test suite that shows how to use it in TecX.Unity.Test).

Advertisement

Register mappings as group

A while ago there was a question on the Unity discussion forum on how to handle a set of mappings as a logical group.

By the time I was neither happy with the implementation nor the API of my proof-of-concept but recently I found some time to improve it.

Consider having interfaces IVehicle, IEngine and IWheel and implementations thereof like Car, MotorCycleWheel and the like. You want to use the MotorCycle* implementations when you ask the container for a motorcycle (via container.Resolve<IVehicle>(“Motorcycle”)). But you don’t want so setup InjectionConstructors or ResolverOverrides.

var container = new UnityContainer();
container.RegisterGroup(c =>
  {
    c.RegisterType<IVehicle, Car>("Car");
    c.RegisterType<IWheel, CarWheel>();
    c.RegisterType<IEngine, CarEngine>();
  });
container.RegisterGroup(c =>
  {
    c.RegisterType<IVehicle, Motorcycle>("Motorcycle");
    c.RegisterType<IWheel, MotorcycleWheel>();
    c.RegisterType<IEngine, MotorcycleEngine>();
  });

var car = container.Resolve<IVehicle>("Car");
Assert.IsInstanceOfType(car.Wheel, typeof(CarWheel));
Assert.IsInstanceOfType(car.Engine, typeof(CarEngine));

var motorcycle = container.Resolve<IVehicle>("Motorcycle");
Assert.IsInstanceOfType(motorcycle.Wheel, typeof(MotorcycleWheel));
Assert.IsInstanceOfType(motorcycle.Engine, typeof(MotorcycleEngine));

The RegisterGroup extension method takes an Action<IUnityContainer> where you can setup the mappings that belong together. The first mapping needs to be named so that the rest of the mappings in the group can be identified properly. After that the mappings are resolved as a whole.

Get the source code for grouped mappings here (project TecX.Unity folder Groups and the test suite that shows how to use it in TecX.Unity.Test).