Виндзорский замок странное поведение с инъекцией свойства и фабричный метод - PullRequest
8 голосов
/ 09 декабря 2010

Я использую Castle Windsor 2.5.1 в проекте ASP.NET MVC и использую инъекцию свойств для создания объекта, который, как я ожидаю, всегда будет доступен в базовом классе контроллера. Я использую фабрику для создания этого объекта, однако, если в конструкторе есть ошибка, я вообще не получаю предупреждение от Windsor, и он просто возвращает мой объект, но без внедрения свойства.

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

Вот пример

public class MyDependency : IMyDependency
{
    public MyDependency(bool error)
    {
        if (error) throw new Exception("I error on creation");
    }
}

public interface IMyDependency
{
}

public class MyConsumer
{
    public IMyDependency MyDependency { get; set; }
}

[TestFixture]
public class ProgramTest
{
    [Test]
    public void CreateWithoutError() //Works as expected
    {
        var container = new WindsorContainer().Register(
            Component.For<IMyDependency>().UsingFactoryMethod(() => new MyDependency(false)).LifeStyle.Transient,
            Component.For<MyConsumer>().LifeStyle.Transient
        );

        var consumer = container.Resolve<MyConsumer>();

        Assert.IsNotNull(consumer);
        Assert.IsNotNull(consumer.MyDependency);
    }

    [Test]
    public void CreateWithError_WhatShouldHappen() //I would expect an error since it can't create MyDependency
    {
        var container = new WindsorContainer().Register(
            Component.For<IMyDependency>().UsingFactoryMethod(() => new MyDependency(true)).LifeStyle.Transient,
            Component.For<MyConsumer>().LifeStyle.Transient
        );

        Assert.Throws<Exception>(() => container.Resolve<MyConsumer>());
    }

    [Test]
    public void CreateWithError_WhatActuallyHappens() //Gives me back a consumer, but ignores MyDependency
    {
        var container = new WindsorContainer().Register(
            Component.For<IMyDependency>().UsingFactoryMethod(() => new MyDependency(true)).LifeStyle.Transient,
            Component.For<MyConsumer>().LifeStyle.Transient
        );

        var consumer = container.Resolve<MyConsumer>();

        Assert.IsNotNull(consumer);
        Assert.IsNull(consumer.MyDependency); //Basically fails silently!
    }
}

Интересное наблюдение: если я использую это в своем приложении MVC, я получаю внутреннюю ошибку от Windsor при вызове ReleaseComponent - поэтому, даже если он не вернул мне класс с введенной зависимостью, он все равно попробуйте выпустить его.

Ответы [ 3 ]

5 голосов
/ 10 декабря 2010

Помимо варианта, предложенного Маурисио, также возможно создание объекта для достижения ожидаемого поведения, как описано на этой странице примера об объектах.

Вот моя реализация, которая немного более краткая:

[AttributeUsage(AttributeTargets.Property, AllowMultiple = false)]
public class NonOptionalAttribute : Attribute
{
}

public class NonOptionalPropertiesFacility : AbstractFacility
{
    protected override void Init()
    {
        Kernel.ComponentModelBuilder.AddContributor(new NonOptionalInspector());
    }
}

public class NonOptionalInspector : IContributeComponentModelConstruction
{
    public void ProcessModel(IKernel kernel, ComponentModel model)
    {
        foreach (var prop in model.Properties.Where(prop => prop.Property.IsDefined(typeof (NonOptionalAttribute), false)))
        {
            prop.Dependency.IsOptional = false;
        }
    }
}

Затем просто украсьте любые свойства с помощью [NonOptional], и вы получите сообщение об ошибке, если возникнет проблема с конструкцией.

5 голосов
/ 09 декабря 2010

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

Конечно, вы всегда можете переопределить активатор по умолчанию своим, если хотите изменить это поведение.

0 голосов
/ 09 января 2014

Начиная с Castle Windsor 3.2, есть новое классное дополнение, Диагностическая регистрация в контейнере .

Так что если вы делаете это, например, в приложении ASP.NET MVC:

var logger = _container.Resolve<ILogger>();
((IKernelInternal)_container.Kernel).Logger = logger;

вы можете перенаправить логи, пойманные Windsor, в ваш сконфигурированный log4net logger.

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

  • Когда Windsor пытается разрешить необязательную зависимость (например, внедрение свойства), но не удается из-за исключение регистрируется.
  • При регистрации типа по соглашению и игнорировании его из-за существующей регистрации для этого типа этот факт регистрируется.
...