Beginner Question - IEnumerable<T>

Jul 31, 2013 at 11:25 AM
Hi, I use the following to register a type:
container.RegisterType<IShopService, ShopService>();
I can then inject this in the constructor e.g.
public ShopController(IShopService shopService) {
    ...
}
However i'd like to register multiple implentations of the same interface and return a list of them. For example say I declare the IEventHandler interface multiple times. I'd like to be able to say:
public EventInvoker(IEnumerable<IEventHandler> eventHandlers) {
    ...
}
However this throws an error about IEnumarable not having a constructor. I'd appreciate it if someone could show me what i'm doing wrong. Thanks
Jul 31, 2013 at 11:30 AM
Replace IEnumerable<IEventHandler> with IEventHandler[] (array of event handlers) and Unity will be able to handle your scenario. Unity will then automatically resolve all named registrations of IEventHandler. But it will not include the default-(unnamed)registration.
Jul 31, 2013 at 12:51 PM
Oh right. I guess this is not going to do what I want. Only option I thought was to look through all the referenced assemblies and look for any type which implements IEventHandler manually. I had hoped Unity could help me do this as I believe Autofac supports it.
Editor
Jul 31, 2013 at 2:45 PM
Edited Aug 1, 2013 at 3:53 AM
If you are using Unity 3, then registration by convention can help in this case:
    public interface IEventHandler { }

    public class EventHandler1 : IEventHandler { }
    public class EventHandler2 : IEventHandler { }
    public class EventHandler3 : IEventHandler { }

    public class EventInvoker
    {
        public EventInvoker(IEventHandler[] eventHandlers) { }
    }

    IUnityContainer container = new UnityContainer();

    container.RegisterTypes(
            AllClasses.FromLoadedAssemblies()
              .Where(t => typeof(IEventHandler).IsAssignableFrom(t)),
            WithMappings.FromAllInterfaces,
            WithName.TypeName);

    // Injected with the 3 IEventHandler implementations
    var eventInvoker = container.Resolve<EventInvoker>();

In the above example, because all three IEventHandler concrete classes are registered with a named registration then Unity handles the array resolution for you. If you want to stay with IEnumerable<IEventHandler> then you would have to register a type mapping explicitly:
    public class EventInvoker
    {
        public EventInvoker(IEnumerable<IEventHandler> eventHandlers) { }
    }

    IUnityContainer container = new UnityContainer();

    container.RegisterTypes(
            AllClasses.FromLoadedAssemblies()
              .Where(t => typeof(IEventHandler).IsAssignableFrom(t)),
            WithMappings.FromAllInterfaces,
            WithName.TypeName);

    // Let Unity know to resolve IEnumerable<IEventHandler> to an Array of IEventHandlers
    container.RegisterType(typeof(IEnumerable<IEventHandler>),typeof(IEventHandler[]));

    // Injected with IEnumerable<IEventHandler>
    var eventInvoker = container.Resolve<EventInvoker>();

~~
Randy Levy
entlib.support@live.com
Enterprise Library support engineer
Support How-to
Aug 1, 2013 at 6:28 AM
One thing I never understood is why Unity excludes the default mapping when resolving multiple dependencies of the same Type (ResolveAll behaves just the same). I know that by now that is established behavior and changing it would most possibly break existing code. But I think it is at least unexpected when you are new to Unity.

I'm really interested in the rationale behind that decision.
Aug 1, 2013 at 12:18 PM
Thanks randylevy. This looks more like it.
Editor
Aug 2, 2013 at 6:10 AM
@weberse, I would guess that we won't find out unless @ctavares decides to post some thoughts.

~~
Randy Levy
entlib.support@live.com
Enterprise Library support engineer
Support How-to
Editor
Aug 8, 2013 at 3:31 PM
Edited Aug 22, 2013 at 6:06 PM
One thing I never understood is why Unity excludes the default mapping when resolving multiple dependencies of the same Type (ResolveAll behaves just the same).
Maybe it was a decision to make it more likely to avoid a StackOverflowException from circular references?
    public class GrandTotalProcessor : IProcessor
    {
        public GrandTotalProcessor(IProcessor[] processors)
        {
        }
    }

In that case, it makes sense:
    container
        .RegisterType<IProcessor, GrandTotalProcessor>()
        .RegisterType<IProcessor, TaxProcessor>("ProcessorArrayItem1")
        .RegisterType<IProcessor, DutyProcessor>("ProcessorArrayItem2");

    IProcessor processor = container.Resolve<IProcessor>();
Now when resolving the default IProcessor the named registrations are injected as an array without trying to construct a GrandTotalProcessor and causing a StackOverflowException.

~~
Randy Levy
entlib.support@live.com
Enterprise Library support engineer
Support How-to
Aug 31, 2013 at 6:04 PM
Edited Aug 31, 2013 at 6:05 PM
@randylevy
For the EventInvoker example, what if all of the following are true?:
  • the constructor which takes the sequence only wants a subset of the implementations
  • you are using design-time configuration
Editor
Sep 1, 2013 at 5:52 AM
Edited Sep 2, 2013 at 7:02 PM
@smarts, in that scenario then I don't think you can inject a subset of IEnumerable using XML configuration. The easiest way to support the scenario would be to change the EventInvoker constructor to accept an array of IEventHandler instead of IEnumerable<IEventHandler>. If that was done then you could manage since Unity understands arrays:
      <register type="IEventHandler" mapTo="EventHandler1" name="EventHandler1" />
      <register type="IEventHandler" mapTo="EventHandler2" name="EventHandler2" />
      <register type="IEventHandler" mapTo="EventHandler3" name="EventHandler3" />
      <register type="EventInvoker">
        <constructor>
          <param name="eventHandlers">
            <array>
              <dependency name="EventHandler1" />
              <dependency name="EventHandler3" />
            </array>
          </param>
        </constructor>        
      </register>

Another approach would be to user a ResolverOverride to inject the desired values:
var invoker = container.Resolve<EventInvoker>(
    new ParameterOverride("eventHandlers", 
        container.ResolveAll<IEventHandler>()
            .Skip(1)
            .Take(2))
);

Another approach would be to change the EventInvoker constructor to accept a factory that knows how to return the correct IEnumerable<IEventHandler>.

Yet another approach that could work would be to use a TypeConverter and to somehow specify the IEventHandler instances to inject as a string.

~~
Randy Levy
entlib.support@live.com
Enterprise Library support engineer
Support How-to
Sep 4, 2013 at 9:00 PM
Thanks, that makes sense. I tend to overload constructors that take IEnumerable<T> to also take params T[] as well, so that works fine for me.