Позволяет ли Light Inject передавать параметры конструктору при разрешении? Я хотел бы знать, делают ли обе эти платформы то, что делают Unity ResolverOverride или DependencyOverride.
Как передать параметр конструктору с помощью Light Inject?
Ответы (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>();
Разница в том, что этот подход не позволяет передавать значение во время разрешения. Значение статически указывается во время регистрации.
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)
{
}
}
Расширенный вариант внедрения примитивных зависимостей подробно описан здесь
Все вышеперечисленное будет работать, если ваш конструктор не имеет других зависимостей (или вы хотите разрешить эти зависимости вручную). Если у вас есть сценарий ниже, хотя он падает:
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)
{
}
}
В ответ на ответ Лиама я хотел бы указать, что есть более простой способ сделать это.
Если у вас следующая ситуация:
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"
)
);
Test
вдруг понадобился еще один сервис — вы заходите в его конструктор и добавляете еще один параметр IBar bar
. Решение @Liam будет работать без изменений, для вашего потребуется отредактировать регистрационный код контейнера.
- person Quercus; 20.11.2020