温莎城堡属性注入和工厂方法的奇怪行为
-
10-10-2019 - |
题
我在 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!
}
}
一个有趣的观察,如果我在我的 MVC 应用程序中使用它,我在调用时会从 Windsor 收到内部错误 ReleaseComponent
- 所以即使它没有给我返回一个注入了依赖项的类,它似乎仍然尝试释放它。
解决方案
据我所知,是的,这就是预期的行为。这不是特定于工厂方法的,它的工作方式与所有可选服务依赖项的工作方式相同。解析时抛出的可选依赖项被视为不可解析。这是定义在 DefaultComponentActivator.ObtainPropertyValue()
当然,如果您想更改此行为,您始终可以用自己的激活器覆盖默认激活器。
其他提示
除了 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;
}
}
}
然后用以下方法装饰任何属性 [NonOptional]
如果施工出现问题,您将收到错误消息。
从温莎城堡 3.2 开始,新增了一个很酷的功能,那就是 容器中的诊断日志记录.
因此,如果您在 ASP.NET MVC 应用程序中执行此操作:
var logger = _container.Resolve<ILogger>();
((IKernelInternal)_container.Kernel).Logger = logger;
您可以将 Windsor 捕获的日志重定向到您配置的 log4net 记录器。
当前记录的信息类型包括:
- 当温莎试图解决可选的依赖性(如财产注入)时,但由于例外而失败时,将记录例外。
- 当按照约定注册类型并由于该类型的现有注册而忽略它时,会记录此事实。
不隶属于 StackOverflow