Как передать параметр конструктору с помощью Light Inject?

Позволяет ли Light Inject передавать параметры конструктору при разрешении? Я хотел бы знать, делают ли обе эти платформы то, что делают Unity ResolverOverride или DependencyOverride.


person Ray    schedule 14.02.2014    source источник
comment
Я думаю, что лучше разделить ваш вопрос на два отдельных вопроса (по одному на каждую структуру). Это упростит вам принятие одного правильного ответа и упростит в будущем для всех остальных поиск в Google для рассматриваемого контейнера.   -  person Steven    schedule 15.02.2014


Ответы (4)


Я подозреваю, что этот вопрос касается передачи примитивных значений конструктору во время фактического разрешения службы.

Создадим простой тестовый класс:

public interface IFoo
{

}

public class Foo : IFoo
{
    public Foo(string value)
    {

    }
}

Класс Foo принимает строковый аргумент, который мы хотели бы предоставить при разрешении службы IFoo.

var container = new ServiceContainer();
container.Register<string, IFoo>((factory, value) => new Foo(value));
var firstFoo = container.GetInstance<string, IFoo>("SomeValue");
var secondFoo = container.GetInstance<string, IFoo>("AnotherValue");

Если мы хотим иметь возможность создавать новые экземпляры класса Foo без непосредственного использования контейнера, мы можем просто внедрить делегат функции.

public interface IBar { }

public class Bar : IBar
{
    public Bar(Func<string, IFoo> fooFactory)
    {
        var firstFoo = fooFactory("SomeValue");
        var secondFoo = fooFactory("AnotherValue");
    }
}

«Корень композиции» теперь выглядит так:

var container = new ServiceContainer();
container.Register<string, IFoo>((factory, value) => new Foo(value));
container.Register<IBar, Bar>();
var bar = container.GetInstance<IBar>();

Если вопрос касается передачи «статического» примитивного значения конструктору, это просто делается путем регистрации фабричного делегата, подобного этому.

var container = new ServiceContainer();
container.Register<IFoo>((factory) => new Foo("SomeValue"));
var firstInstance = container.GetInstance<IFoo>();
var secondInstance = container.GetInstance<IFoo>();

Разница в том, что этот подход не позволяет передавать значение во время разрешения. Значение статически указывается во время регистрации.

person seesharper    schedule 15.02.2014
comment
Все в порядке. Мне просто не нравится строка container.Register‹string, IFoo›((factory, value) =› new Foo(value)); Что, если у Foo есть какие-то другие зависимости IService1,...,IServiceN, которые должны быть внедрены контейнером? Я хотел бы просто сказать, просто используйте определенную строку для этого конкретного параметра, поскольку контейнер остальных параметров должен предоставлять зависимости. Как я могу это сделать? - person user1325696; 27.07.2015
comment
А как насчет container.Register<IFoo>((factory) => new Foo(container.GetInstance<ISomeOtherDependency>(), "SomeValue"));? - person Marcello; 20.03.2016

Вероятно, самый простой вариант с Simple Injector — зарегистрироваться с помощью делегата.

[Test]
public void Test1()
{
    Container container = new Container();

    container.Register<IClassWithParameter>(() => new ClassWithParameter("SomeValue"));

    var result = container.GetInstance<IClassWithParameter>();
}

public interface IClassWithParameter { }

public class ClassWithParameter : IClassWithParameter
{
    public ClassWithParameter(string parameter)
    {
    }
}

Расширенный вариант внедрения примитивных зависимостей подробно описан здесь

person qujck    schedule 14.02.2014

Все вышеперечисленное будет работать, если ваш конструктор не имеет других зависимостей (или вы хотите разрешить эти зависимости вручную). Если у вас есть сценарий ниже, хотя он падает:

public class Test : ITest
{
   private IFoo _foo;
   public Test(string parameter, IFoo foo)
   {
      _foo = foo;
      ....
   }
}

Теперь вам нужно не только вручную ввести строку, но и Foo. Итак, теперь вы вообще не используете внедрение зависимостей (на самом деле). Также состояние простого инжектора:

Simple Injector не позволяет внедрять примитивные типы (такие как целые числа и строки) в конструкторы.

Я читаю это так, что они говорят: «Не делай этого».

Точки расширения

Другой вариант — использовать для этого сценария "точки расширения".

Для этого вам нужно абстрагировать ваши жестко закодированные элементы от ваших введенных элементов:

public class Test : ITest
{
   private IFoo _foo;
   public Test(IFoo foo)
   {
      _foo = foo;
      ....
   }

  public void Init(string parameter)
  {

  }
}

Теперь вы можете внедрить свои зависимости и жестко запрограммированные элементы:

_container.Register<ITest, Test>();
_container.RegisterInitializer<Test>(instance => {instance.Init("MyValue");});

Если теперь вы добавите еще одну зависимость, ваша инъекция теперь будет работать без необходимости обновлять конфигурацию, т. е. ваш код все еще хорошо отделен:

public class Test : ITest
{
   private IFoo _foo;
   private IBar _bar;
   public Test(IFoo foo, IBar bar)
   {
      _foo = foo;
      _bar = bar;
      ....
   }

  public void Init(string parameter)
  {

  }
}
person Liam    schedule 29.06.2017

В ответ на ответ Лиама я хотел бы указать, что есть более простой способ сделать это.

Если у вас следующая ситуация:

public class Test : ITest
{
   private IFoo _foo;
   public Test(IFoo foo, string parameter)
   {
      _foo = foo;
      ....
   }
}

Вы можете написать свою конфигурацию ioc, как показано ниже.

_container.Register<IFoo, Foo>();
_container.Register<ITest>(
    () => new Test(
        _container.GetInstance<IFoo>(),
        "MyValue"
    )
);
person Jan Coppens    schedule 20.11.2020
comment
Это возможно, но это не совсем DI-стиль. Представьте, что вашему классу Test вдруг понадобился еще один сервис — вы заходите в его конструктор и добавляете еще один параметр IBar bar. Решение @Liam будет работать без изменений, для вашего потребуется отредактировать регистрационный код контейнера. - person Quercus; 20.11.2020
comment
Привет Quercus, спасибо за ваш отзыв! Вы определенно правы. - person Jan Coppens; 23.11.2020
comment
Я думаю, что в обоих решениях что-то есть. Если вы выберете решение в стиле DI, вам придется манипулировать существующим классом, добавив метод Init + вы также будете зависеть от DI, выполняющего метод Init после его создания (другой разработчик, не знающий о вашем классе, может забыть использовать метод Init). Init при создании класса). Я бы предпочел использовать параметр в конструкторе, чтобы убедиться, что параметр действительно установлен, прежде чем что-то делать с классом. - person Jan Coppens; 23.11.2020