Статьи

Unity: передача параметров конструктора для разрешения

В этом уроке мы рассмотрим несколько различных способов использования пользовательских параметров конструктора при разрешении экземпляра в Unity:

  1. С помощью встроенного ParameterOverride
  2. Создавая собственный ResolverOverride.

Фон

Когда вы используете DI-контейнер, такой как Unity , вам обычно не нужно беспокоиться о том, как контейнер разрешает новый экземпляр. Вы настроили контейнер, и контейнер будет действовать в зависимости от вашей конфигурации. Но могут быть случаи, когда вы передаете пользовательские параметры конструктора для операции разрешения. Некоторые могут утверждать, что это кричит о плохой архитектуре, но есть ситуации, например, перенос DI-контейнера в унаследованную систему, которая может требовать такого рода действий.

Решенный класс

В этом уроке мы решаем следующий тестовый класс:


public class MyClass
{
    public string Hello { get; set; }
    public int Number { get; set; }
 
    public MyClass(string hello, int number)
    {
        Hello = hello;
        Number = number;
    }

Он регистрируется в контейнере с помощью метода RegisterType и без передачи каких-либо параметров:

 
var unity = new UnityContainer();
unity.RegisterType<MyClass>();

Итак, давайте посмотрим, как мы можем передать переменные «hello» и «number» для конструктора MyClass ‘при вызове Unity’s Resolve.

Unity ResolverOverride

Unity позволяет нам передавать ResolverOverride, когда вызывается метод Resolve- контейнера . ResolverOverride является абстрактным базовым классом, и Unity поставляется с несколькими из них встроенными. Одним из них является ParameterOverride, который «позволяет переопределять именованный параметр, передаваемый в конструктор». 

Таким образом, зная, что нам нужно передать строку с именем «hello» и целое число с именем «number», мы можем разрешить экземпляр с помощью ParameterOverride:

[Test]
public void Test()
{
    var unity = new UnityContainer();
    unity.RegisterType<MyClass>();
 
    var myObj = unity.Resolve<MyClass>(new ResolverOverride[]
                                   {
                                       new ParameterOverride("hello", "hi there"), new ParameterOverride("number", 21)
                                   });
 
    Assert.That(myObj.Hello, Is.EqualTo("hi there"));
    Assert.That(myObj.Number, Is.EqualTo(21));
}

Мы передаем в двух случаях ParameterOverride. Оба из них принимают имя и значение параметра.

Custom ResolverOverride: OrderedParametersOverride

Но что, если вам не нравится передавать имена параметров и вместо этого вы хотите передать только значения параметров в правильном порядке? Для этого мы можем создать собственный ResolverOverride. Вот один из способов сделать это:

public class OrderedParametersOverride : ResolverOverride
{
    private readonly Queue<InjectionParameterValue> parameterValues;
 
    public OrderedParametersOverride(IEnumerable<object> parameterValues)
    {
        this.parameterValues = new Queue<InjectionParameterValue>();
        foreach (var parameterValue in parameterValues)
        {
            this.parameterValues.Enqueue(InjectionParameterValue.ToParameter(parameterValue));
        }
    }
 
    public override IDependencyResolverPolicy GetResolver(IBuilderContext context, Type dependencyType)
    {
        if (parameterValues.Count < 1)
            return null;
 
        var value = this.parameterValues.Dequeue();
        return value.GetResolverPolicy(dependencyType);
    }

Значения параметров передаются через конструктор и помещаются в очередь. Когда контейнер разрешает экземпляр, параметры используются в том порядке, в котором они были переданы OrderedParametersOverride.

Вот пример использования нового OrderedParametersOverride:

[Test]
public void TestOrderedParametersOverride()
{
    var unity = new UnityContainer();
    unity.RegisterType<MyClass>();
 
    var myObj = unity.Resolve<MyClass>(new OrderedParametersOverride(new object[] {"greetings", 24 }));
 
    Assert.That(myObj.Hello, Is.EqualTo("greetings"));
    Assert.That(myObj.Number, Is.EqualTo(24));
}

Образец кода

Приведенные выше примеры можно найти на GitHub.