Castle Windsor: заставляет решатель использовать указанный конструктор - PullRequest
7 голосов
/ 28 октября 2011

Вот пример:

interface IComponentA {};

class ComponentA : IComponentA { };

interface IComponentB { };

class ComponentB : IComponentB { };

interface IComponentC { };

class ComponentC : IComponentC
{
    public ComponentC(IComponentA a)
    {
        Console.WriteLine("Constructor A"); 
    }

    public ComponentC(IComponentB b) 
    {
        Console.WriteLine("Constructor B");
    }
};

Все эти компоненты зарегистрированы в контейнере Castle Windsor.

Но класс ComponentC имеет 2 перегруженных конструктора. Любой из них может быть использован при активации ComponentC.

Мне нужен ComponentC(IComponentB b) конструктор.

На данный момент я использую метод UsingFactoryMethod () для решения этой проблемы:

container
    .Register(Component
        .For<IComponentA>()
        .ImplementedBy<ComponentA>())
    .Register(Component
        .For<IComponentB>()
        .ImplementedBy<ComponentB>())
    .Register(Component
        .For<IComponentC>()
        .UsingFactoryMethod(() => new ComponentC(
            container.Resolve<IComponentB>())));

Это работает, но, возможно, Касл Виндзор предлагает какой-то лучший способ сделать это?

Любая помощь очень ценится.

Спасибо.

Ответы [ 2 ]

6 голосов
/ 04 сентября 2012

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

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

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

Другой сценарий, который я видел, выглядит примерно так:

public class Foo
{
   public Foo(ISession session){/*code*/}
   public Foo(ISessionFactory factory):this(factory.OpenSession()){}
}

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

1 голос
/ 17 ноября 2015

Хм, это ужасно, но есть один способ (в прошлом мне приходилось делать это с объектами Linq2Sql DataContext).Вы создаете класс декоратора и регистрируете его вместо этого.

Допустим, у вас есть этот интерфейс:

public interface IService 
{
    void DoSomething();
}

И у вас есть реализация следующим образом:

public class Service : IService
{
    private readonly ILogger _logger;

    public Service(ILogger logger)
        : this(logger, SomeDefaultListOfThings())
    {
    }

    // Let's say Windsor is calling this ctor for some reason (ArrayResolver?)
    public Service(ILogger logger, IEnumerable<object> emptyArrayFromWindsor)
    {
        _logger = logger;
        PutTheItemsSomewhere(emptyArrayFromWindsor);
    }

    public void DoSomething()
    {
        // Something that relies on the list of items...
    }
}

Нокак показывает пример, по какой-то причине Виндзор вызывает неправильный ctor, и вы не можете убедить его в обратном (как верно указывает Кшиштоф).Затем вы можете создать класс декоратора следующим образом, который имеет только один конструктор и, таким образом, устраняет неоднозначность:

public class SpecificCtorServiceDecorator : IService
{
    private readonly IService _decorated;

    public SpecificCtorServiceDecorator(ILogger logger)
    {
        _decorated = new Service(logger);
    }

    public void DoSomething()
    {
        _decorated.DoSomething();
    }
}

Затем вы зарегистрируете этот класс вместо:

container.Register(
    Component.For<IService>()
             .ImplementedBy<SpecificCtorServiceDecorator>());

Конечно, было бы лучше, если бы не происходила эта странная вещь по умолчанию-значения-в-других-конструкторах ( поиск "Внедрение зависимости бедного человека" ), но в том случае, если выВы не управляете классом, который вам действительно нужен (как я был в Linq2Sql, или если это будет серьезное изменение API), тогда это может избавить вас от неприятностей.

...