Типизированное фабричное средство создания рекурсивно вложенных объектов

Рассмотрим ситуацию, когда вам нужно создать некоторые рекурсивно вложенные вещи, например, такие:

    public interface IRecurrentTestNodeFactory
    {
        RecurrentTestNode Create(int num);
    }

    public class RecurrentTestNode
    {
        public int Num { get; set; }
        public RecurrentTestNode Child { get; set; }

        public RecurrentTestNode(int num, IRecurrentTestNodeFactory factory)
        {
            Num = num;
            Child = num > 0 ? factory.Create(num - 1) : null;
        }
    }

Очевидная реализация такова:

    public class ManualRecurrentTestNodeFactory : IRecurrentTestNodeFactory
    {
        public RecurrentTestNode Create(int num)
        {
            return new RecurrentTestNode(num, this);
        }
    }

    [Test]
    public void ManualRecurrentTest()
    {
        var root = new ManualRecurrentTestNodeFactory().Create(1);
        Assert.NotNull(root);
        Assert.AreEqual(1, root.Num);
        Assert.NotNull(root.Child);
        Assert.AreEqual(0, root.Child.Num);
        Assert.Null(root.Child.Child);
    }

Этот тест проходит. Но если вы попытаетесь сделать то же самое с Виндзорской фабрикой типизированных фабрик, вот так:

    [Test]
    public void RecurrentTest()
    {
        var windsor = new WindsorContainer();
        windsor.Kernel.AddFacility<TypedFactoryFacility>();
        windsor.Register(Component.For<IRecurrentTestNodeFactory>().AsFactory());
        windsor.Register(Component.For<RecurrentTestNode>().LifeStyle.Transient);

        var f = windsor.Resolve<IRecurrentTestNodeFactory>();
        var root = f.Create(1);
        Assert.NotNull(root);
        Assert.AreEqual(1, root.Num);
        Assert.NotNull(root.Child);
        Assert.AreEqual(0, root.Child.Num);
        Assert.Null(root.Child.Child);
    }

Это терпит неудачу с этими исключениями:

Castle.MicroKernel.ComponentActivator.ComponentActivatorException : ComponentActivator: could not instantiate Tests.RecurrentTestNode
  ----> System.Reflection.TargetInvocationException : Exception has been thrown by the target of an invocation.
  ----> Castle.MicroKernel.CircularDependencyException : Dependency cycle has been detected when trying to resolve component 'Tests.RecurrentTestNode'.
The resolution tree that resulted in the cycle is the following:
Component 'Tests.RecurrentTestNode' resolved as dependency of
    component 'Tests.RecurrentTestNode' which is the root component being resolved.

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

1 ответ

Решение

Делайте это лениво, чтобы разорвать цикл, а не в конструкторе. Поведение Виндзора правильное.

Другие вопросы по тегам