【问题标题】:How to configure Unity to inject an array for IEnumerable如何配置 Unity 为 IEnumerable 注入数组
【发布时间】:2011-12-15 11:54:07
【问题描述】:

我有一个类,它接受一个 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 配置执行什么操作才能使其正常工作?

【问题讨论】:

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


【解决方案1】:

这是我找到的一个解决方案,但它有点笨拙。您需要一个包装类来帮助 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 并注入它吗?

【讨论】:

    【解决方案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 的语法,但我相信你也可以在那里实现它。

    【讨论】:

      【解决方案3】:

      Unity 配置没有默认支持 IEnumerable&lt;T&gt; 依赖项。使用现有统一配置来实现目标的唯一替代方法是将IEnumerable&lt;T&gt; 映射到 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&lt;T&gt; 依赖解析。

      【讨论】:

      • 您可以通过可在 TecX 项目中找到的容器扩展添加对 IEnumerable、ICollection 和 IList 的支持。 CollectionResolutionExtension 的源代码位于 TecX.Unity 项目中。
      • 通过将ThingArray 的定义更改为SimpleThing[],我设法使示例正常工作并注入了一个空数组,但是如何定义EnumerableThing 以包含四个SimpleThing?跨度>
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2023-04-06
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多