C# Castle Windsor奇怪的行为,带有属性注入和工厂方法

C# Castle Windsor奇怪的行为,带有属性注入和工厂方法,c#,castle-windsor,ioc-container,microkernel,C#,Castle Windsor,Ioc Container,Microkernel,我在ASP.NET MVC项目中使用Castle Windsor 2.5.1,并使用属性注入创建一个对象,我希望该对象在基本控制器类上始终可用。我使用工厂创建这个对象,但是如果构造函数中有错误,我根本不会收到Windsor的警告,它只返回我的对象,但不注入属性 这是预期的行为吗?如果是,当工厂未能返回任何内容时,我如何获得错误 这里有一个例子 public class MyDependency : IMyDependency { public MyDependency(bool erro

我在ASP.NET MVC项目中使用Castle Windsor 2.5.1,并使用属性注入创建一个对象,我希望该对象在基本控制器类上始终可用。我使用工厂创建这个对象,但是如果构造函数中有错误,我根本不会收到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!
    }
}
公共类MyDependency:IMyDependency
{
公共MyDependency(布尔错误)
{
if(error)抛出新异常(“I创建时出错”);
}
}
公共接口不依赖性
{
}
公共类消费者
{
公共IMyDependency MyDependency{get;set;}
}
[测试夹具]
公共类程序测试
{
[测试]
public void CreateWithoutError()//按预期工作
{
var container=new WindsorContainer().寄存器(
Component.For().UsingFactoryMethod(()=>new MyDependency(false)).LifeStyle.Transient,
Component.For().LifeStyle.Transient
);
var consumer=container.Resolve();
Assert.IsNotNull(使用者);
Assert.IsNotNull(consumer.MyDependency);
}
[测试]
public void CreateWithError\u WhatShouldHappen()//我预计会出现错误,因为它无法创建MyDependency
{
var container=new WindsorContainer().寄存器(
Component.For().UsingFactoryMethod(()=>new MyDependency(true)).LifeStyle.Transient,
Component.For().LifeStyle.Transient
);
抛出(()=>container.Resolve());
}
[测试]
public void CreateWithError_WhatActuallyHappens()//返回消费者,但忽略我的依赖关系
{
var container=new WindsorContainer().寄存器(
Component.For().UsingFactoryMethod(()=>new MyDependency(true)).LifeStyle.Transient,
Component.For().LifeStyle.Transient
);
var consumer=container.Resolve();
Assert.IsNotNull(使用者);
Assert.IsNull(consumer.MyDependency);//基本上是无声地失败的!
}
}

一个有趣的观察结果是,如果我在MVC应用程序中使用它,我会在调用
ReleaseComponent
时从Windsor得到一个内部错误——因此,即使它没有给我返回一个注入依赖项的类,它似乎仍然试图释放它。

据我所知,是的,这是预期的行为。这并不特定于工厂方法,它对所有可选的服务依赖项都是这样工作的。 解析时抛出的可选依赖项被视为不可解析。 这是在中定义的


当然,如果您想更改此行为,您可以始终使用自己的激活器覆盖默认激活器。

除了Mauricio建议的选项外,还可以创建一个设施,以实现有关设施的示例页面中所述的预期行为

下面是我的实现,它稍微简洁一些:

[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;
        }
    }
}

然后只需使用
[非可选]
装饰任何属性,如果存在构造问题,您将收到一个错误。

从Castle Windsor 3.2开始,有一个新的很酷的添加

因此,如果您在ASP.NET MVC应用程序中执行此操作:

var logger = _container.Resolve<ILogger>();
((IKernelInternal)_container.Kernel).Logger = logger;
var记录器=_container.Resolve();
((IKernelInternal)u container.Kernel).Logger=Logger;
您可以将Windsor捕获的日志重定向到配置的log4net记录器

当前正在记录的信息类型包括:

  • 当Windsor尝试解决可选依赖项(如属性注入)时,由于 对于异常,将记录该异常
  • 按约定注册类型并由于该类型的现有注册而忽略该类型时,会记录此事实

谢谢,答案很准确!我可以用几种方法解决这个问题,所以这不是一个完美的问题!如果任何阅读过的人都记不起安装工具的语法(比如我),只需在引导例程中调用Container.AddFacility()