Castle Windsor IOC: передача параметров конструктора дочерним компонентам - PullRequest
6 голосов
/ 11 октября 2010

Следующий код предназначен только для демонстрационных целей.

Допустим, у меня есть 2 компонента (businessService и dataService) и класс пользовательского интерфейса.

Класс пользовательского интерфейсанужна бизнес-служба, businessService нужна dataService, а dataService полагается на connectionString.

Сформируйте класс пользовательского интерфейса, который необходим для разрешения бизнес-службы, поэтому я пишу следующий код:

var service = container.Resolve<BusinessService>(new { dependancy = "con string 123" }));

обратите внимание, что зависимость является параметром конструктора connectionString.

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

Невозможно создать компонент 'dataService', так как он имеет зависимости, которые должны быть удовлетворены.dataService ожидает следующих зависимостей:

Ключи (компоненты с определенными ключами) - зависимость, которая не была зарегистрирована.

Так что в качестве обходного пути я делаю это:

var service = container.Resolve<BusinessService>(new { dataService = container.Resolve<IDataService>(new { dependancy = "123" }) });

Но из-за дизайна, стиля кодирования и многих других аспектов это не очень хороший способ сделать это.

Поэтому, пожалуйста, если вы можете посоветовать, почему это не работает простым способом, или у вас есть лучший обходной путьПожалуйста, поделитесь.

Ответы [ 3 ]

6 голосов
/ 11 октября 2010

Поведение, которое вы видите, является умышленным.

Существует несколько способов решения проблемы, в зависимости от того, насколько динамичным является значение, которое вы хотите передать.

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

Обновление

Для ясности - Виндзор не передает встроенные аргументы по конвейеру разрешения. Причина этого проста - это нарушит абстракцию. Код вызова должен неявно знать, что ваш BusinessService зависит от DataService, который зависит от строки подключения.

Если вам абсолютно необходимо это сделать, тогда сделайте это явно. Это в значительной степени то, что вы делаете - разрешите DataService с его зависимостью от строки подключения явным образом и явно разрешите BusinessService, передав DataService как зависимость.

Чтобы сделать вещи действительно явными (и приятнее в использовании), я бы предложил использовать для этого Typed Factory вместо прямого вызова контейнера

public interface IFactory
{
   IDataService ResolveDataService(string connectionString);
   IBussinessService ResolveBussinessService(IDataService dataService);
   // possibly release method for IBussinessService as well
}
2 голосов
/ 07 декабря 2010

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

public class ArgumentPassingDependencyResolver : DefaultDependencyResolver
{
    protected override CreationContext RebuildContextForParameter(
        CreationContext current, Type parameterType)
    {
        if (parameterType.ContainsGenericParameters)
        {
            // this behaviour copied from base class
            return current;
        }

        // the difference in the following line is that "true" is passed
        // instead of "false" as the third parameter
        return new CreationContext(parameterType, current, true);
    }
}

Экземпляр этого класса должен быть передан при создании контейнера (другие классы также должны быть переданы, потому что нет удобного конструктора, который принимает только преобразователь зависимостей):

var container = new WindsorContainer(
    new DefaultKernel(
        new ArgumentPassingDependencyResolver(),
        new NotSupportedProxyFactory()),
    new DefaultComponentInstaller());
0 голосов
/ 19 января 2018

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

С типизированными фабриками все, что вам нужно сделать, это определить фабричные интерфейсы, и Windsor позаботится о реализации для вас.

public interface IBusinessServiceFactory {
  IBusinessService CreateBusinessService(string connString);
}

public interface IDataServiceFactory {
  IDataService CreateDataService(string connString);
}

Вы добавляете объект и регистрируете свой интерфейс фабрики следующим образом:

container.AddFacility<TypedFactoryFacility>();
container.Register(Component.For<IDataServiceFactory>().AsFactory());
container.Register(Component.For<IBusinessServiceFactory>().AsFactory());

Теперь вы можете вручную определять, как ваш параметр времени выполнения передается по графу объектов, определяя Динамический параметр в вашей BusinessService регистрации.

container.Register(Component.For<IBusinessService, BusinessService>()
    .LifestyleTransient()
    .DynamicParameters((k, d) => {
        d["dataService"] = k.Resolve<IDataServiceFactory>.CreateDataService((string)d["connString"]);
    }));

Имейте в виду, что ключи словаря должны соответствовать именам параметров в методе CreateBusinessService и конструкторе BusinessService.

Вы также должны сделать это LifestyleTransient , если вы собираетесь создавать новый экземпляр каждый раз, когда вызывается метод фабрики. (По умолчанию используется синглтон)

...