Back to blog

Making Unity work more like the others

April 16, 2009 - Posted in c# Posted by:

Tags: , ,

I love MS Unity as an IoC container, it has some great features. Though I found it to function incorrectly at two points (it’s not that the path chosen by the Unity team is incorrect, I just want/excpect it to act differently).

The first thing that bothered me was the fact that Unity throws an exception when Resolve<T> is not able to resolve the given type: I’d prefer it to return null instead, hence most containers do that. The second, even more irritating point, was that ResolveAll<T> only returned the named instances/types instead of all registered types. In fact, Unity overwrites the default registration if your register a type multiple times. Eg:

    
Container.Register<IMyInterFace, MyClass1>();
Container.Register<IMyInterFace, MyClass2>();

would cause the MyClass1 registration to be removed. I’d like the MyClass1 to remain the default registration, and have ResolveAll<IMyInterface>() return both. I’m happy to tell you that it can be done!

When using an IoC container, it’s good practice to access it’s features through a generic wrapper class. For my projects, I use the wrapper from the Kigg source code. If you access the container through a wrapper after you configured your container, it’ll be easy to replace it with a different container in the future.

My wrapper class looks like this:

    
public class UnityDependencyResolver : IDependencyResolver
{
    private readonly IUnityContainer container;

    public UnityDependencyResolver() : this(new UnityContainer())
    {
        var configuration = (UnityConfigurationSection) ConfigurationManager.GetSection("unity");
        if (configuration != null)
            configuration.Containers.Default.Configure(container);
    }
    
    public UnityDependencyResolver(IUnityContainer container)
    {
        Check.Argument.IsNotNull(container, "container");

        this.container = container;
        this.container.AddExtension(new TypeTrackingExtension());
        this.container.RegisterInstance<IUnityContainer>(container);
        this.container.RegisterInstance<IDependencyResolver>(this);
    }

    public IUnityContainer Container { get { return container; } }

    public void RegisterInstance<T>(T instance)
    {
        Check.Argument.IsNotNull(instance, "instance");

        container.RegisterInstance(instance);
    }

    public void RegisterType<TFrom, TTo>() where TTo : TFrom
    {
        container.RegisterType<TFrom, TTo>();
    }

    public void RegisterType(Type from, Type to)
    {
        Check.Argument.IsNotNull(from, "from");
        Check.Argument.IsNotNull(to, "to");

        container.RegisterType(from, to);
    }
    
    public void Inject<T>(T existing)
    {
        Check.Argument.IsNotNull(existing, "existing");

        container.BuildUp(existing);
    }
    
    public object Resolve(Type type)
    {
        Check.Argument.IsNotNull(type, "type");
        if (container.Configure<TypeTrackingExtension>().CanResolve(type))
        {
            return container.Resolve(type);
        }
        else
        {
            return null;
        }
    }
    
    public object Resolve(Type type, string name)
    {
        Check.Argument.IsNotNull(type, "type");
        if (Container.Configure<TypeTrackingExtension>().CanResolve(type, name))
        {
            return container.Resolve(type, name);
        }
        else
        {
            return null;
        }
    }

    
    public T Resolve<T>()
    {
        return (T) Resolve(typeof(T));
    }

    
    public T Resolve<T>(string name)
    {
        Check.Argument.IsNotEmpty(name, "name");
        return (T) Resolve(typeof(T), name);
    }

    
    public IEnumerable<T> ResolveAll<T>()
    {
        var namedInstances = container.ResolveAll<T>();
        var unnamedInstance = default(T);

        try
        {
            unnamedInstance = container.Resolve<T>();
        }
        catch (ResolutionFailedException)
        {
            //When default instance is missing
        }

        if (Equals(unnamedInstance, default(T)))
        {
            return namedInstances;
        }

        return new ReadOnlyCollection<T>(new List<T>(namedInstances) { unnamedInstance });
    }
    
    ~UnityDependencyResolver()
    {
        Dispose(false);
    }
    public void Dispose()
    {
        Dispose(true);
    }
    protected void Dispose(bool disposing)
    {
        if (disposing)
        {
            container.Dispose();
        }
    }
}

Though most methods are just direct wrappers for accessing the container itself, there are a few extra’s. For one, there’s the ResolveAll<T> method that returns both named and unnamed instances instead of only the named instances, that’s one problem fixed.

Secondly, you can see the Resolve methods use

if (Container.Configure<TypeTrackingExtension>().CanResolve(type, name))

It uses an UnityExtension to see if the Type can be resolved and it returns null if it can’t. This solution was provided by David Buksbaum in his blog post Type Tracking Extension for Unity. This extension is added to the container in this wrapper’s constructor.

I did modify David’s extension a little, to have it not overwrite the first registered type and to have CanResolve return true for types that can be initiated also (the original only returns true for types that have been registered). My version looks like this:

public class TypeTrackingExtension : UnityContainerExtension
{
    private readonly Dictionary<Type, HashSet<string>> registeredTypes = new Dictionary<Type, HashSet<string>>();

    protected override void Initialize()
    {
        Context.Container.Configure<UnityDefaultBehaviorExtension>().Remove();
        Context.Container.Configure<InjectedMembers>().Remove();
        //we have to be the first in the chain
        Context.RegisteringInstance += OnNewInstance;
        Context.Registering += OnNewType;

        Context.Container.Configure<UnityDefaultBehaviorExtension>().InitializeExtension(Context);
        Context.Container.Configure<InjectedMembers>().InitializeExtension(Context);
    }

    public override void Remove()
    {
        base.Remove();
        Context.RegisteringInstance -= OnNewInstance;
        Context.Registering -= OnNewType;
    }
    private void OnNewInstance(object sender, RegisterInstanceEventArgs e)
    {
        HashSet<string> names;
        string name = string.IsNullOrEmpty(e.Name) ? string.Empty : e.Name;

        if (!registeredTypes.TryGetValue(e.RegisteredType, out names))
        { //  not found, so add it
            registeredTypes.Add(e.RegisteredType, new HashSet<string> { name });
        }
        else
        { //  already added type, so add name
            if (name == String.Empty && names.Contains(name))
            {
                //default instance is already registered, let's give it a name so we can use ResolveAll() like it works in all other ioc containers
                name = e.Name = Guid.NewGuid().ToString();
            }
            names.Add(name);
        }
    }
    private void OnNewType(object sender, RegisterEventArgs e)
    {
        HashSet<string> names;
        string name = string.IsNullOrEmpty(e.Name) ? string.Empty : e.Name;
        if (!registeredTypes.TryGetValue(e.TypeFrom, out names))
        { //  not found, so add it
            registeredTypes.Add(e.TypeFrom, new HashSet<string> { name });
        }
        else
        { //  already added type, so add name
            if (name == String.Empty && names.Contains(name))
            {
                //default instance is already registered, let's give it a name so we can use ResolveAll() like it works in all other ioc containers
                name = e.Name = Guid.NewGuid().ToString();
            }
            names.Add(name);
        }
    }

    /// <summary>
    /// Determines whether this type can be resolved as the default.
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <returns>
    /// 	<c>true</c> if this instance can resolve; otherwise, <c>false</c>.
    /// </returns>
    public bool CanResolve<T>()
    {
        return CanResolve(typeof(T), null);
    }
    /// <summary>
    /// Determines whether this type can be resolved as the default.
    /// </summary>
    /// <param name="type">The type.</param>
    /// <returns>
    /// 	<c>true</c> if this instance can resolve; otherwise, <c>false</c>.
    /// </returns>
    public bool CanResolve(Type type)
    {
        return CanResolve(type, null);
    }
    /// <summary>
    /// Determines whether this type can be resolved with the specified name.
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <param name="name">The name.</param>
    /// <returns>
    /// 	<c>true</c> if this instance can be resolved with the specified name; otherwise, <c>false</c>.
    /// </returns>
    public bool CanResolve<T>(string name)
    {
        return CanResolve(typeof (T), name);
    }

    /// <summary>
    /// Determines whether this type can be resolved with the specified name.
    /// </summary>
    /// <param name="type">The type.</param>
    /// <param name="name">The name.</param>
    /// <returns>
    /// 	<c>true</c> if this instance can be resolved with the specified name; otherwise, <c>false</c>.
    /// </returns>
    public bool CanResolve(Type type, string name)
    {
        if (isResolvableClass(type))
            return true;

        HashSet<string> names;
        if (registeredTypes.TryGetValue(type, out names))
        {
            return names.Contains(name ?? string.Empty);
        }
        return false;
    }

    private bool isResolvableClass(Type type)
    {
        return type.IsClass && !type.IsAbstract;
    }
    /// <summary>
    /// Determines whether this instance can be resolved at all.
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <returns>
    /// 	<c>true</c> if this instance can be resolved at all; otherwise, <c>false</c>.
    /// </returns>
    public bool CanResolveAny<T>()
    {
        return CanResolveAny(typeof(T));
    }

    /// <summary>
    /// Determines whether this instance can be resolved at all.
    /// </summary>
    /// <param name="type">The type.</param>
    /// <returns>
    /// 	<c>true</c> if this instance can be resolved at all; otherwise, <c>false</c>.
    /// </returns>
    public bool CanResolveAny(Type type)
    {
        return isResolvableClass(type) || registeredTypes.ContainsKey(type);
    }

    /// <summary>
    /// Tries to resolve the type, returning null if not found.
    /// </summary>
    /// <typeparam name="T">The type to try and resolve.</typeparam>
    /// <returns>An object of type <see cref="T"/> if found, or <c>null</c> if not.</returns>
    public T TryResolve<T>()
    {
        return TryResolve<T>(default(T));
    }
    /// <summary>
    /// Tries to resolve the type with the specified of name, returning null if not found.
    /// </summary>
    /// <typeparam name="T">The type to try and resolve.</typeparam>
    /// <param name="name">The name associated with the type.</param>
    /// <returns>An object of type <see cref="T"/> if found, or <c>null</c> if not.</returns>
    public T TryResolve<T>(string name)
    {
        return TryResolve<T>(name, default(T));
    }
    /// <summary>
    /// Tries to resolve the type, returning null if not found.
    /// </summary>
    /// <typeparam name="T">The type to try and resolve.</typeparam>
    /// <param name="defaultValue">The default value to return if type not found.</param>
    /// <returns>An object of type <see cref="T"/> if found, or the <see cref="defaultValue"/> if not.</returns>
    public T TryResolve<T>(T defaultValue)
    {
        if (!CanResolve<T>())
            return defaultValue;
        return Container.Resolve<T>();
    }
    /// <summary>
    /// Tries to resolve the type with the specified of name, returning null if not found.
    /// </summary>
    /// <typeparam name="T">The type to try and resolve.</typeparam>
    /// <param name="name">The name associated with the type.</param>
    /// <param name="defaultValue">The default value to return if type not found.</param>
    /// <returns>An object of type <see cref="T"/> if found, or the <see cref="defaultValue"/> if not.</returns>
    public T TryResolve<T>(string name, T defaultValue)
    {
        if (!CanResolve<T>(name))
            return defaultValue;
        return Container.Resolve<T>(name);
    }
}

As you can see, I change the order of the event chain, by removing UnityDefaultBehaviorExtension from the Container, and then add it again later. This allows us to add a name to the type being registered. If a type is registered multiple times without a name given, a name is given in the form of a unique Guid

So now, if you do

    
Container.Register<IMyInterFace, MyClass1>();
Container.Register<IMyInterFace, MyClass2>();

var wrapper = new UnityDependencyResolver(Container);
var classes = wrapper.ResolveAll<IMyInterFace>()

classes will contain both MyClass1 and MyClass2!

7 Comments

Ray 5 years ago

I’m not sure why you would want to register the same interface with two different default (?), i.e. unnamed Types. Can you give a use case?

Chris van de Steeg 5 years ago

@Ray: sure, let’s say you have a an interface IBootable with 1 method : Execute
In your application startup you can say

foreach(IBootable bootable in IoC.ResolveAll())
bootable.Execute()

Ray 5 years ago

@Chris: Thanks. So it’s more that you don’t care about their names rather than having a ‘default’ implementation, you simply want to treat them all implementations of the interface as a collection.

Alex 3 years ago

Nice post. With the new IDependencyResolver in ASP.NET MVC 3, this may be the way to go. The MVC framework expects your IDependencyResolver to return null if it can’t resolve a given type. Currently, the approach people are taking is to wrap the Resolve call in a try/catch which seems kinda ugly. This approach, while a lot of extra code, seems to be a better way to go. I kinda wish it was built into Unity though…

Joel 8 months ago

Try this:

Container.Register(“MyClass1″);
Container.Register(“MyClass2″);

Joel 8 months ago

Eugh. That din’t work. The blog stripped out my generic arguments.

Container.Register less than IMyInterFace, MyClass1 greater than();
Container.Register less than IMyInterFace, MyClass2 greater than();

Joel 8 months ago

I messed up again.

My point is, if you give each registration a string name, it will behave like you want.

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>