11

My goal is to constructor inject an array of objects implementing an interface.

The following is the way I currently have it.

Container

        .RegisterInstance<Company>(ParseCompany(args[1])

        .RegisterInstance<eTargets>(ParseTargets(args[2]))

        .RegisterInstance<ILoader[]>(new ILoader[] {
            Container.Resolve<CustomerLoader>(),
            Container.Resolve<PaymentLoader(),
            Container.Resolve<InvoiceLoader() 
        });

Is it typical to call Resolve in container configuration this way or is there a more standard way to accomplish the same thing?

meJustAndrew
  • 6,011
  • 8
  • 50
  • 76
Aaron Anodide
  • 16,906
  • 15
  • 62
  • 121

3 Answers3

30

Unity natively understands arrays, so there's no reason to make it so complicated. Just register the ILoaders you want to include and resolve the object graphs normally. Auto-wiring will take care of the rest:

container.RegisterType<ILoader, FooLoader>("foo");
container.RegisterType<ILoader, BarLoader>("bar");
container.RegisterType<ILoader, BazLoader>("baz");

var c = container.Resolve<MyConsumer>();

assuming that the MyConsumer constructor is defined like this:

public MyConsumer(ILoader[] loaders)

However, you should be aware that (for some unfathomable reason) Unity only includes named components in this way. The default component:

container.RegisterType<ILoader, Loader>();

will not be included in the array, since it has no name.

Mark Seemann
  • 225,310
  • 48
  • 427
  • 736
  • 1
    Not exactly unfathomable: the reason is that, if you need more than one entry for the same type, then you must add a name otherwise Unity will think you're actually overriding the previous entry (which is forbidden). – Bruno Brant Apr 15 '15 at 17:55
  • 5
    @BrunoBrant 'unfathomable' because no other DI Container works like that. See e.g. [my book](http://amzn.to/12p90MG) for more details. – Mark Seemann Apr 15 '15 at 18:09
4

If you have a slightly more complicated scenario where different arrays of values have to be used in different places, then you can use ResolvedArrayParameter, e.g.

container.RegisterType<ILoader, FooLoader>("foo");
container.RegisterType<ILoader, BarLoader>("bar");
container.RegisterType<ILoader, BazLoader>("baz");
container.RegisterType<ILoader, BooLoader>("boo");

container.RegisterType<IConsumer, MyConsumer>("c1",
    new InjectionConstructor(
        new ResolvedArrayParameter<ILoader>(
            new ResolvedParameter<ILoader>("foo"),
            new ResolvedParameter<ILoader>("bar"))));

container.RegisterType<IConsumer, MyConsumer>("c2",
    new InjectionConstructor(
        new ResolvedArrayParameter<ILoader>(
            new ResolvedParameter<ILoader>("baz"),
            new ResolvedParameter<ILoader>("boo"))));

var c1 = container.Resolve<MyConsumer>("c1");
var c1 = container.Resolve<MyConsumer>("c2");
Paul Hatcher
  • 7,342
  • 1
  • 54
  • 51
3

Using Resolve during "configuration time" is acceptable and often useful, and it's perfectly valid for arrays or enumerables.

You could also have done the above by registering the ILoader[] type and registering each of the specific ILoader types using the RegisterType overload that takes a name.

Then, wherever ILoader[] is required (e.g. needs to be injected), all of the above will resolve for you after configuration time. Of course, if you need multiple/different ILoader[] it would devolve back to needing to use Resolve during configuration time.

Kit
  • 20,354
  • 4
  • 60
  • 103