问题
I have a class which takes an IEnumerable
constructor parameter which I want to resolve with Unity and inject an array of objects. These simple classes illustrate the problem.
public interface IThing
{
int Value { get; }
}
public class SimpleThing : IThing
{
public SimpleThing()
{
this.Value = 1;
}
public int Value { get; private set; }
}
public class CompositeThing : IThing
{
public CompositeThing(IEnumerable<IThing> otherThings)
{
this.Value = otherThings.Count();
}
public int Value { get; private set; }
}
Say I want to inject four SimpleThing
in to CompositeThing
. I've tried several variations on the following Unity configuration.
<alias alias="IThing" type="TestConsoleApplication.IThing, TestConsoleApplication" />
<alias alias="SimpleThing" type="TestConsoleApplication.SimpleThing, TestConsoleApplication" />
<alias alias="CompositeThing" type="TestConsoleApplication.CompositeThing, TestConsoleApplication" />
<container>
<register type="IThing" mapTo="SimpleThing" name="SimpleThing" />
<register type="IThing" mapTo="CompositeThing" name="CompositeThing">
<constructor>
<param name="otherThings">
<array>
<dependency type="SimpleThing"/>
<dependency type="SimpleThing"/>
<dependency type="SimpleThing"/>
<dependency type="SimpleThing"/>
</array>
</param>
</constructor>
</register>
</container>
However I get the error message The configuration is set to inject an array, but the type IEnumerable`1 is not an array type. If I change the constructor parameter to be IThing[]
it works, but I don't want to do that. What do I need to do to my Unity configuration to get this to work?
回答1:
Here's one solution that I've found, but it's a bit naff. You need a wrapper class to help Unity recognise that an array is IEnumerable
.
public class ArrayWrapper<T> : IEnumerable<T>
{
public ArrayWrapper(T[] things)
{
this.things = things;
}
private IEnumerable<T> things;
IEnumerator<T> IEnumerable<T>.GetEnumerator()
{
return this.things.GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator()
{
return this.things.GetEnumerator();
}
}
You can then configure Unity to inject one of these, using Ethan's EnumberableThing
idea.
<alias alias="IThing" type="TestConsoleApplication.IThing, TestConsoleApplication" />
<alias alias="SimpleThing" type="TestConsoleApplication.SimpleThing, TestConsoleApplication" />
<alias alias="CompositeThing" type="TestConsoleApplication.CompositeThing, TestConsoleApplication" />
<alias alias="EnumerableThing" type="System.Collections.Generic.IEnumerable`1[[TestConsoleApplication.IThing, TestConsoleApplication]], mscorlib"/>
<alias alias="ThingArrayWrapper" type="TestConsoleApplication.ArrayWrapper`1[[TestConsoleApplication.IThing, TestConsoleApplication]], TestConsoleApplication"/>
<container>
<register type="EnumerableThing" mapTo="ThingArrayWrapper">
<constructor>
<param name="things">
<array>
<dependency type="SimpleThing"/>
<dependency type="SimpleThing"/>
<dependency type="SimpleThing"/>
<dependency type="SimpleThing"/>
</array>
</param>
</constructor>
</register>
<register type="IThing" mapTo="SimpleThing" name="SimpleThing" />
<register type="IThing" mapTo="CompositeThing" name="CompositeThing">
<constructor>
<param name="otherThings" dependencyType="EnumerableThing" />
</constructor>
</register>
</container>
Like I say though, a bit naff and awkward when you want another CompositeThing
with three, five, six things etc.
Surely Unity should be able to recognise that an array is IEnumberable
and inject it?
回答2:
Actually Unity understands arrays. The accepted solution will work great, but if you don't want the wrapper and as long as you want to fetch all registered implementations it's as easy as:
public class CompositeThing : IThing
{
public CompositeThing(IThing[] otherThings)
{
this.Value = otherThings.Count();
}
public int Value { get; private set; }
}
The downside is that it will probably also return an instance if itself since it's implementing IThing.
Similiar question here: How to inject IEnumerable using Microsoft Unity IOC container
Just make sure to register the implementations with a specific name, as such:
container.RegisterType<IThing, FooThing>("Foo");
container.RegisterType<IThing, BarThing>("Bar");
I don't know the syntax for XML, but I'm sure you can implement it there as well.
回答3:
There is no default support of IEnumerable<T>
dependency for Unity configuration. The only alternative using the existing unity configuration to achieve your target is as to map a IEnumerable<T>
to T[], as follows.
<unity xmlns="http://schemas.microsoft.com/practices/2010/unity">
<alias alias="IThing" type="TestConsoleApplication.IThing, TestConsoleApplication" />
<alias alias="SimpleThing" type="TestConsoleApplication.SimpleThing, TestConsoleApplication" />
<alias alias="CompositeThing" type="TestConsoleApplication.CompositeThing, TestConsoleApplication" />
<alias alias="EnumerableThing" type="System.Collections.Generic.IEnumerable`1[[TestConsoleApplication.IThing, TestConsoleApplication]], mscorlib"/>
<alias alias="ThingArray" type="TestConsoleApplication.IThing[], TestConsoleApplication"/>
<container>
<register type="EnumerableThing" mapTo="ThingArray"/>
<register type="IThing" mapTo="SimpleThing" name="SimpleThing" />
<register type="IThing" mapTo="CompositeThing">
<constructor>
<param name="otherThings" dependencyType="EnumerableThing"/>
</constructor>
</register>
</container>
C# code is:
IUnityContainer container = new UnityContainer();
container.LoadConfiguration();
IThing thing = container.Resolve<CompositeThing>();
Console.WriteLine(thing.Value);
If you do not want this way, i think you can extend Unity Configuration System to support IEnumerable<T>
dependency resolving, by implementing a new EnumerableElement inherit ParameterValueElement.
来源:https://stackoverflow.com/questions/8519709/how-to-configure-unity-to-inject-an-array-for-ienumerable