如何配置Unity为IEnumerable注入数组

时间:2011-12-15 11:54:07

标签: dependency-injection inversion-of-control unity-container constructor-injection

我有一个类,它接受一个IEnumerable构造函数参数,我想用Unity解析并注入一个对象数组。这些简单的类说明了这个问题。

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; }
}

假设我想向SimpleThing注入四个CompositeThing。我已经尝试了以下Unity配置的几种变体。

<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>

但是我收到错误消息配置设置为注入数组,但IEnumerable`1类型不是数组类型。如果我将构造函数参数更改为IThing[]它有效,但我不想这样做。我需要对Unity配置做些什么才能使其正常工作?

3 个答案:

答案 0 :(得分:3)

这是我找到的一个解决方案,但它有点勉强。您需要一个包装类来帮助Unity识别数组是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();
    }
}

然后,您可以使用Ethan的EnumberableThing想法将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="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>

就像我说的那样,当你想要另外CompositeThing有三个,五个,六个等等时,有点尴尬和尴尬。

当然,Unity应该能够识别数组是IEnumberable并注入它吗?

答案 1 :(得分:2)

实际上Unity了解数组。接受的解决方案效果很好,但如果您不想要包装器,只要您想要获取所有已注册的实现,它就像以下一样简单:

public class CompositeThing : IThing
{
    public CompositeThing(IThing[] otherThings)
    {
        this.Value = otherThings.Count();
    }

    public int Value { get; private set; }
}

缺点是它可能还会返回一个实例,因为它实现了IThing。

这里有类似的问题: How to inject IEnumerable using Microsoft Unity IOC container

请确保使用特定名称注册实现,如下所示:

container.RegisterType<IThing, FooThing>("Foo");
container.RegisterType<IThing, BarThing>("Bar");

我不知道XML的语法,但我相信你也可以在那里实现它。

答案 2 :(得分:0)

Unity配置没有IEnumerable<T>依赖项的默认支持。使用现有统一配置实现目标的唯一选择是将IEnumerable<T>映射到T [],如下所示。

<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#代码是:

 IUnityContainer container = new UnityContainer();
 container.LoadConfiguration();
 IThing thing = container.Resolve<CompositeThing>();
 Console.WriteLine(thing.Value);

如果您不想这样,我认为您可以通过实现新的EnumerableElement继承ParameterValueElement来扩展Unity配置系统以支持IEnumerable<T>依赖项解析。