Как настроить Unity для внедрения массива для IEnumerable

У меня есть класс, который принимает параметр конструктора 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, чтобы это заработало?


comment
возможный дубликат Разрешение IEnumerable‹T› с Unity   -  person Mark Seemann    schedule 15.12.2011
comment
Я не думаю, что это дубликат, потому что этот вопрос касается программной регистрации, тогда как я использую файл конфигурации.   -  person batwad    schedule 15.12.2011


Ответы (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();
    }
}

Затем вы можете настроить Unity для внедрения одного из них, используя идею Итана EnumberableThing.

<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 и внедрять его?

person batwad    schedule 16.12.2011

На самом деле Unity понимает массивы. Принятое решение будет отлично работать, но если вам не нужна оболочка и если вы хотите получить все зарегистрированные реализации, это так же просто, как:

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

    public int Value { get; private set; }
}

Недостатком является то, что он, вероятно, также вернет экземпляр, если сам, поскольку реализует IThing.

Аналогичный вопрос здесь: Как внедрить IEnumerable с помощью Microsoft Контейнер IOC Unity

Просто не забудьте зарегистрировать реализации с определенным именем, как таковое:

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

Я не знаю синтаксиса XML, но уверен, что вы сможете реализовать его и там.

person smoksnes    schedule 13.01.2016

По умолчанию не поддерживается зависимость IEnumerable<T> для конфигурации Unity. Единственной альтернативой использованию существующей конфигурации единства для достижения вашей цели является сопоставление 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# code is:

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

Если вы не хотите этого, я думаю, вы можете расширить систему конфигурации Unity для поддержки разрешения зависимостей IEnumerable<T>, внедрив новый EnumerableElement, наследующий ParameterValueElement.

person Ethan Wu    schedule 16.12.2011
comment
Вы можете добавить поддержку IEnumerable‹T›, ICollection‹T› и IList‹T› через расширение контейнера, которое можно найти в TecX проект. Исходный код для CollectionResolutionExtension находится внутри проекта TecX.Unity. - person Sebastian Weber; 16.12.2011
comment
Изменив определение ThingArray на SimpleThing[], мне удалось заставить пример работать и внедрить пустой массив, но как я могу определить EnumerableThing, чтобы оно содержало четыре SimpleThing? - person batwad; 16.12.2011