bckpwrld
bckpwrld

Reputation: 1219

Injected generic array doesn't contain any components

Code is taken from. Unity should resolve all named registrations of Account type and put them into a generic array. But for some reason named Account dependencies are not resolved and thus the injected generic array is empty. Why is array not populated?

    container.RegisterType<Account>("first");
    container.RegisterInstance<Account>("second", new Account());

    container.RegisterType(
        typeof(MyClass<>),
        new InjectionConstructor(
            new GenericResolvedArrayParameter("T")));

    var myClass = container.Resolve<MyClass<Account>>();
    Console.WriteLine(myClass.InjectedValue.Length);   // 0

public class Account
{ }

public class MyClass<T>
{
    public T[] injectedValue;

    public MyClass(T[] injectedValue)
    {
        this.InjectedValue = injectedValue;
    }

    public T[] InjectedValue
    {
        get { return this.injectedValue; }
        set { this.injectedValue = value; }
    }
}

thanks

Upvotes: 1

Views: 167

Answers (1)

Daniel J.G.
Daniel J.G.

Reputation: 34992

The code you are using will just help Unity choosing a constructor that takes a single generic parameter T. However it does not tell which instances to provide.

You can use GenericResolvedArrayParameter to specify the named instances you want injected as in:

var container = new UnityContainer();
container.RegisterType<Account>("first");
container.RegisterInstance<Account>("second", new Account());

container.RegisterType(
    typeof(MyClass<>),  
    new InjectionConstructor(
        new GenericResolvedArrayParameter(
            "T",
            new GenericParameter("T", "first"),
            new GenericParameter("T", "second"))));

However this is a bit cumbersome, and only really useful if you have multiple constructors (As you specify which constructor should Unity use) or if you just want to pass some of the named registrations (as you manually include them). Luckily Unity understand arrays by default, and will provide all named instances. So if you have a single constructor taking T[] you don't need the registration for MyClass at all (as it is a concrete type):

var container = new UnityContainer();
container.RegisterType<Account>("first"); 
container.RegisterInstance<Account>("second", new Account());
var defaultInstance = new Account();
container.RegisterInstance<Account>(defaultInstance);

Just by doing that you could try to resolve MyClass<Account>, and you would see the named registrations "first" and "second" were injected.

var myClass = container.Resolve<MyClass<Account>>();
//myClass.injectedValue contains 2 items, from registrations "first" and "second"
//default (unnamed) registration is skipped

If instead of a constructor with an array parameter T[] you have another enumerable type like IEnumerable<T>, you can still use this last approach. Just add another registration that tells Unity how to map from arrays to the desired enumerable type:

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

I have created this fiddle testing the 4 approaches. Hopefully it will be useful to understand how these options work.

Upvotes: 1

Related Questions