Resolving IEnumerable<T> with Unity
Asked Answered
C

8

43

Can Unity automatically resolve IEnumerable<T>?

Let's say I have a class with this constructor:

public CoalescingParserSelector(IEnumerable<IParserBuilder> parserBuilders)

and I configure individual IParserBuilder instances in the container:

container.RegisterType<IParserSelector, CoalescingParserSelector>();
container.RegisterType<IParserBuilder, HelpParserBuilder>();
container.RegisterType<IParserBuilder, SomeOtherParserBuilder>();

can I make this work without having to implement a custom implementation of IEnumerable<IParserBuilder>?

var selector = container.Resolve<IParserSelector>();

So far I haven't been able to express this in any simple way, but I'm still ramping up on Unity so I may have missed something.

Cuticle answered 25/12, 2009 at 17:26 Comment(0)
C
66

It turns out that this is actually awfully simple to do:

container.RegisterType<IEnumerable<IParserBuilder>, IParserBuilder[]>();

Unity natively understands arrays, so we just need to map the enumerable to an array of the same type.

Cuticle answered 8/9, 2010 at 13:11 Comment(6)
Greate solution! This is better than all other options.Sideline
And for filling up the IEnumerable for constructor injection (which i hoped to see in answers here), please refer to following question. #6304855Appomattox
Too bad you need to add a new registration for each type. If only there was something as simple as container.RegisterType(GetType(IEnumerable(Of )), GetType(Array(Of )))Yorgen
As has been mentioned below, if using multiple instances you will need to include the unique names as well. Otherwise the List will contain zero items: container.RegisterType<IParserBuilder, HelpParserBuilder>("HelpParserBuilder");Noshow
And what would be the way to pass this to an InjectionConstructor?Alvira
This solution will only work if you name your registrations! i.e. it won't work as declared by OP in his question description! Must change to container.RegisterType<IParserBuilder, HelpParserBuilder>("A");, etc...Susy
U
13

@Metro Smurf: your answer got me in the right track: Unity is unable to automatically resolve IEnumerable<T> dependencies.

I wasn't able to compile your example since the RegisterType method doesn't take an InjectionConstructor instance as parameter.

Also note that the ResolveAll method will only work if you've registered multiple types with different names and also this method does NOT return an instance for the default (unnamed) registration. (I completely disagree with this behavior btw).

This is what worked for me:

container.RegisterType<IParserBuilder, HelpParserBuilder>("HelpParserBuilder");
container.RegisterType<IParserBuilder, SomeOtherParserBuilder>("SomeOtherParserBuilder");
container.RegisterType<IParserSelector, CoalescingParserSelector>();

container.Configure<InjectedMembers>().ConfigureInjectionFor<CoalescingParserSelector>(new InjectionConstructor(container.ResolveAll<IParserBuilder>()));

In order to resolve a single instance you will need to also add a default registration otherwise the call to Resolve<T>() will fail.

This code makes the default registration to enable single resolution:

container.RegisterType<IParserBuilder, HelpParserBuilder>();
IParserBuilder builder = container.Resolve<IParserBuilder>()
Unload answered 25/2, 2010 at 16:23 Comment(2)
+1 That works as well, but is neither better nor worse than the other answer.Cuticle
@Mark: If you are registering multiple instances, you would have to supply different instance names.Unfeigned
U
8

If you want to generally support IEnumerable, then you can add this line:

_container.RegisterType(typeof(IEnumerable<>), new InjectionFactory((IUnityContainer container, Type type, string name) => container.ResolveAll(type.GetGenericArguments().Single())));

This is he generic version of

container.RegisterType<IEnumerable<IParserBuilder>, IParserBuilder[]>();
Urbani answered 25/9, 2014 at 8:3 Comment(1)
I love the simple one liner here. This is very useful until Unity adds native support for IEnumerable<T> constructor injection.Generation
B
6

I believe you'll need to use the ResolveAll method and use an explicit InjectionConstructor object, i.e.:

container.RegisterType<IParserBuilder, HelpParserBuilder>();
container.RegisterType<IParserBuilder, SomeOtherParserBuilder>();

var injectedBuilders = new InjectionConstructor(container.ResolveAll<IParserBuilder>());
container.RegisterType<IParserSelector, CoalescingParserSelector>(injectedBuilders);

In other words, I don't think Unity is able to automatically resolve all instances of a type and know to use constructor injection on a class with an IEnumerable parameter without an explicitly declaring an InjectionConstructor object at Run Time.

Granted I'm still learning Unity as well, but this has been my experience (YMMV).

Breakup answered 25/12, 2009 at 21:56 Comment(2)
+1 Not perfect, but definitely better than what I originally had.Cuticle
Main problem I had with this solution is that the instances are resolved on the unity container setup thread which was no good to me, I needed a new set of injected items each resolution. But thanks for putting me on the right track.Tole
R
6

The

container.RegisterType<IEnumerable<IParserBuilder>, IParserBuilder[]>();

Worked for me only keep in mind that when you register IParserBuilder types with your container an unique name is required otherwise it will Always be an empty array. So use

container.RegisterType<IParserBuilder, RealParserBuilder>("UniqueName");
Rissole answered 26/8, 2013 at 8:47 Comment(0)
M
4

As of May 2010, there is the native support for that. Check it out here.

Mainz answered 13/5, 2010 at 22:10 Comment(1)
While it is nice that resolution of IEnumerable<T> is supported, constructor injection is not supported yet.Generation
N
4

I did this like so

        container.RegisterTypes(
            AllClasses.FromLoadedAssemblies().
                Where(type => typeof(IParserBuilder).IsAssignableFrom(type)),
            WithMappings.FromAllInterfaces,
            WithName.TypeName,
            WithLifetime.Transient);

        container.RegisterType<IEnumerable<IParserBuilder>, IParserBuilder[]>();

Which registers everything that implements IParserBuilder, so when you add a new one you don't need to add any other code for it to appear in the list of builders.

Neubauer answered 23/12, 2014 at 16:58 Comment(0)
E
0

You can do like this:

container.RegisterType<IParserBuilder, HelpParserBuilder>("HelpParser");
container.RegisterType<IParserBuilder, SomeOtherParserBuilder>("SomeOtherParser");

container.RegisterType<IParserSelector, CoalescingParserSelector>(
 new InjectionConstructor(
                    new ResolvedArrayParameter<IParserBuilder>(
                        new ResolvedParameter<IParserBuilder>("HelpParser"),
                        new ResolvedParameter<IParserBuilder>("SomeOtherParser")
                    )
));
Externalization answered 9/4, 2018 at 10:33 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.