ResolveAll interface types in entire inheritance chain

May 10, 2011 at 9:22 AM
Edited May 10, 2011 at 9:23 AM

I have IMyInterface and the interface it inherits, IParentInterface.

InterfaceImplementer class implements every interface member in the entire inheritance chain.


using System; interface IParentInterface { void ParentInterfaceMethod(); } interface IMyInterface : IParentInterface { void MethodToImplement(); } class InterfaceImplementer : IMyInterface { static void Main() { InterfaceImplementer iImp = new InterfaceImplementer(); iImp.MethodToImplement(); iImp.ParentInterfaceMethod(); } public void MethodToImplement() { Console.WriteLine("MethodToImplement() called."); } public void ParentInterfaceMethod() { Console.WriteLine("ParentInterfaceMethod() called."); } }


I try Register in UnityContainer the InterfaceImplementer Type mapped to IMyInterface:

IUnityCntainer.RegisterType<InterfaceImplementer,IMyInterface>();


When try use the ResolveAll method present in IUnityContainer for IParentInterface, i do not have results. 
var results = IUnityContainer.ResolveAll<IParentInterface>();


This scenario works well in CastleWindsor

Does anyone have any idea to get the same effect in Unity?

May 11, 2011 at 1:32 PM

Hi,

Here are some of the items I've notice on the code posted :o)

  • First, you need to explicitly have a type mapping registered for the IParentInterface to the concrete class InterfaceImplementer to be able to resolve this.
  • Second, in registering a type it should be Interface class map to the concrete class (container.RegisterType<IMyInterface, InterfaceImplementer>()) instead of the other way around.
  • Third, your concrete class InterfaceImplementer should be Public.
  • In using ResolveAll() this only resolve named registration and does not the default ones. You should have something like - "container.RegisterType<IMyInterface, InterfaceImplementer>("MyInterfaceImplementer");"
  • ResolveAll returns an IEnumerable generic list therefore you would need to iterate the resolve type from there.

Hope this helps.

Gino Terrado
Global Technologies and Solutions
Avanade, Inc.
entlib.support@avanade.com

May 11, 2011 at 2:36 PM

Thank for your support.

for the third point, I know it must be public, but I missed to put the word key public.

I found this solution to my problem

 public static class MyUnityExtensionsMethods
    {
         public static IEnumerable<TIComponent> ResolveAllInheritance<TIComponent>(this IUnityContainer container) where TIComponent : class
        {
            List<TIComponent> lst = new List<TIComponent>();
            if (container != null)
            {
                var matches = container.Registrations.Where(r => r.MappedToType.GetInterfaces().Contains(typeof(TIComponent)) == true);

                foreach (var registration in matches)
                {
                    lst.Add(container.Resolve(registration.MappedToType) as TIComponent);
                }                                
            }
            return lst.AsEnumerable<TIComponent>();
        }
    }

There is a more elegant solution?

 

May 12, 2011 at 6:36 PM

I think it looks good though I just would like to understand what are you trying to address here, is it to avoid registering type with name or to iterate thru the IEnumerable list returned by the ResolveAll method? Assuming that the registration with name doesn't matter and that the purpose of the extension method is to only iterate from the result of the resolved object. Then I would think it is much simpler to have something like the following;

container.RegisterType<IMyInterface, InterfaceImplementer>("MyInterfaceImplementer");
            List<IMyInterface> result = container.ResolveAll<IMyInterface>().ToList();

Though, I may be missing the bigger picture here. Anyway just thinking it may somehow help :o)

Gino Terrado
Global Technologies and Solutions
Avanade, Inc.
entlib.support@avanade.com

 

 

May 13, 2011 at 8:54 AM

Gino Thanks for your response.

I want apply ResolveAll on IParentInterface and not on IMyInterface.

my extension method, it works if i pass IParentInterface.

May 18, 2011 at 8:54 AM

That was slick. You're right ;-)

Gino Terrado
Global Technologies and Solutions
Avanade, Inc.
entlib.support@avanade.com

May 20, 2011 at 5:16 PM
Edited May 23, 2011 at 6:42 PM
AvanadeSupport wrote:

In using ResolveAll() this only resolve named registration and does not the default ones. You should have something like - "container.RegisterType<IMyInterface, InterfaceImplementer>("MyInterfaceImplementer");" 

 

You have to be kidding me. ResolveAll method then is horribly broken. The method name is Resolve All. Not Resolve All But Not Defaults. I'm sorry but why on earth did anyone think this is a good idea. No other mainstream container out there works like this. The defacto behavior for this type of method is to GET EVERYTHING BY A GIVEN TYPE. Period. This is a bug at best and a fundamental design flaw at worst. Please tell me this is going to change in a future release.

May 23, 2011 at 3:00 AM

Hello jimmyzimms,

We understand the dilemma but this is what is as designed since Unity 1.2 started. As for the rationale behind this I believe the Entlib team could provide more details on this. Though you can always logged for any feature enhancements and confirmed bug in the issue tracker.

Gino Terrado
Global Technologies and Solutions
Avanade, Inc.
entlib.support@avanade.com

 

May 23, 2011 at 6:52 PM

Considering that I cannot be the only person out there that finds this untenable I’ve whipped up a custom version of the ResolveAll<T>() extension method that will work “as advertised”. Simply plunk this down into your code + namespace (or order this namespace using before unity) and it should resolve this one and not the core implementation. Again, this took me less than 2 minutes to whip out so I cannot figure out why the EnLib team couldn’t do the same.

(for those of you not working with MSR Code Contracts just replace the Contract.Requires() calls to a classic if … throw pattern)

 

    /// <summary>
    /// Extensions for the <see cref="IUnityContainer"/> interface that bring Unity into the defacto standards of other DI containers.
    /// </summary>
    internal static class UnityContainerExtensions
    {
        private sealed class ContainerRegistrationEqualityComparer : EqualityComparer<ContainerRegistration>
        {
            internal readonly static ContainerRegistrationEqualityComparer Instance = new ContainerRegistrationEqualityComparer();

            private ContainerRegistrationEqualityComparer() { }

            public override bool Equals(ContainerRegistration x, ContainerRegistration y)
            {
                return (x.RegisteredType == y.RegisteredType) && (StringComparer.OrdinalIgnoreCase.Equals(x.Name, y.Name));
            }

            public override int GetHashCode(ContainerRegistration obj)
            {
                unchecked
                {
                    return obj.RegisteredType.GetHashCode() + (obj.Name ?? String.Empty).GetHashCode();
                }
            }
        }

        /// <summary>
        /// Overrides the OOB <see cref="Microsoft.Practices.Unity.UnityContainerExtensions"/> implementation.
        /// </summary>
        /// <remarks>
        /// The OOB implementation of this method lacks any support for default registerations and does not walk the container hierarchy
        /// when resolving instances. This version will start at the given container instance, yield all registered instances of 
        /// <typeparamref name="T"/> and then walk the tree, yielding registrations. It is fully aware of registration overrides in child
        /// containers and therefore will not return a registration higher in the tree that has been overriden. In addition, this method
        /// will never throw the <see cref="ResolutionFailedException"/>.
        /// </remarks>
        /// <typeparam name="T">The type of service to resolve.</typeparam>
        /// <param name="container">The <see cref="IUnityContainer"/> instance to resolve from.</param>
        /// <returns>A sequence of all registered instances of <typeparamref=T"/>.</returns>
        internal static IEnumerable<T> ResolveAll<T>(this IUnityContainer container)
        {
            Contract.Requires<ArgumentNullException>(container != null, "container");

            var registrations = new Dictionary<ContainerRegistration, IUnityContainer>(ContainerRegistrationEqualityComparer.Instance);

            var currentContainer = container;
            while (currentContainer != null)
            {
                foreach (var item in container.Registrations.Where(registration => registration.RegisteredType == typeof(T)).Where(item => !registrations.ContainsKey(item)))
                {
                    registrations.Add(item, currentContainer);
                }
                currentContainer = currentContainer.Parent;
            }

            return registrations.Select(registration => (T)registration.Value.Resolve(registration.Key.RegisteredType, registration.Key.Name));
        }
    }