Объединение DI с параметрами конструктора? - PullRequest
32 голосов
/ 04 августа 2011

Как объединить инжекцию конструктора с «ручными» параметрами конструктора?то есть.

public class SomeObject
{
    public SomeObject(IService service, float someValue)
    {
    }
}

Где IService должен быть разрешен / введен моим DI-контейнером, а someValue должно быть указано.Как мне смешать два?

Ответы [ 6 ]

30 голосов
/ 04 августа 2011

Таких конструкций следует избегать, когда это возможно.Поэтому спросите себя: действительно ли этот параметр требуется в качестве аргумента конструктора?Или можно заменить SomeObject на объект без состояния, который используется всеми, кто зависит от него, путем передачи параметра методу, который вы выполняете для объекта?

например, вместо

public class SomeObject
{
    private float someValue
    public SomeObject(IService service, float someValue)
    {
        this.someValue = someValue
    }

    public float Do(float x)
    {
        return this.Service.Get(this.someValue) * x;
    }
}

use

public class SomeObject
{
    public SomeObject(IService service)
    {
    }

    public float Do(float x, float someValue)
    {
        return this.Service.Get(someValue) * x;
    }
}

Если требуется, перейдите на завод:

public interface ISomeObjectFactory
{
    ISomeObject CreateSomeObject(float someValue);
}

public class SomeObjectFactory : ISomeObjectFactory
{
    private IKernel kernel;
    public SomeObjectFactory(IKernel kernel) 
    {
        this.Kernel = kernel;
    }

    public ISomeObject Create(float someValue)
    {
        return this.kernel.Get<ISomeObject>(WithConstructorArgument("someValue", someValue);
    }
}

Предварительный просмотр: Ninject 2.4 больше не будет требовать реализации, но разрешит

kernel.Bind<ISomeObjectFactory>().ToFactory();  // or maybe .AsFactory();
2 голосов
/ 15 марта 2018

Другой подход - инициализация в два этапа (не связанная с njectject, любая структура DI):

public class SomeObject
{
    private readonly IService _service;

    public SomeObject(IService service)
    {
        // constructor only captures dependencies
        _service = service;
    }

    public SomeObject Load(float someValue)
    {
        // real initialization goes here
        // ....

        // you can make this method return no value
        // but this makes it more convienient to use
        return this;
    }
}

и использование:

public static class TestClass
{
    public static void TestMethod(IService service)
    {
        //var someObject = new SomeObject(service, 5f);
        var someObject = new SomeObject(service).Load(5f);
    }
}
2 голосов
/ 04 августа 2011

Вы действительно не должны пытаться использовать D.I. за это. Вы можете придумать все дурацкие решения, но в будущем они могут не иметь смысла.

Наш подход состоит в том, чтобы создать фабрику через D.I., а затем метод Create фабрики создаст себя, используя переданный в D.I. контейнер. Нам не нужно часто использовать этот шаблон, но когда мы это делаем, он на самом деле делает продукт намного чище (поскольку он делает наши графики зависимостей меньше).

1 голос
/ 04 августа 2011

В NInject, с которым вы отметили это, вы вводите автоматически сгенерированную Фабрику в виде Func<parameters you wish to feed in,T>, используя FuncModule, как описано в этом посте .

Этот подход также доступен в autofac для одного.

Различные подходы фабричного метода описаны в ответах на этот вопрос .

РЕДАКТИРОВАТЬ: NB Хотя это может быть интересноПожалуйста, используйте решение @Remo Gloor (и, что самое важное, советуйте избегать такого решения)

1 голос
/ 04 августа 2011

Если 'somevalue' всегда постоянное, тогда вы можете подумать об использовании InjectionParameters, пока вы регистрируете свой тип в контейнере, как описано в посте ниже

См. Здесь

но если это не так, то нет способа разделить значение параметра при разрешении экземпляра, вы можете подумать о том, чтобы удалить 'someValue' из конструктора и сделать его свойством класса.

0 голосов
/ 04 августа 2011

Я бы, наверное, использовал наивное решение для этого.Если вы знаете значение someValue, когда вам это нужно, я бы удалил его из конструктора и добавил свойство к вашему объекту, чтобы вы могли установить someValue.Таким образом, вы можете получить свой объект из вашего контейнера, а затем установить значение, когда у вас есть объект.

Мое другое предложение заключается в том, что вместо прямого доступа к нему вы создаете фабрику, которую можно использовать для создания такого объекта.Затем вы регистрируете фабрику в своем контейнере и используете фабрику для создания вашего экземпляра.Примерно так:

public class SomeObjectFactory : ISomeObjectFactory
{
    private IYourService _service;
    public SomeObjectFactory(IYourService service) 
    {
        _service = service;
    }

    public ISomeObject Create(float someValue)
    {
        return new SomeObject(_service, someValue);
    }
}

вы можете попробовать такой шаблон.

ОБНОВЛЕНИЕ: Обновлен код для отражения комментариев по улучшению.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...